From 3cb43e5d3ee9b5c36e2b9d3cbda2bbc561b60d33 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sun, 22 May 2022 20:00:06 -0600 Subject: [PATCH 01/15] Improve display --- .../ProcessQueue/VirtualFlowControl.Designer.cs | 13 ++++++++++++- .../ProcessQueue/VirtualFlowControl.cs | 14 +++----------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.Designer.cs b/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.Designer.cs index 77f793fc..dfcedef8 100644 --- a/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.Designer.cs +++ b/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.Designer.cs @@ -29,6 +29,7 @@ private void InitializeComponent() { this.panel1 = new System.Windows.Forms.Panel(); + this.vScrollBar1 = new System.Windows.Forms.VScrollBar(); this.SuspendLayout(); // // panel1 @@ -39,12 +40,21 @@ this.panel1.BackColor = System.Drawing.SystemColors.ControlDark; this.panel1.Location = new System.Drawing.Point(0, 0); this.panel1.Name = "panel1"; - this.panel1.Size = new System.Drawing.Size(377, 505); + this.panel1.Size = new System.Drawing.Size(357, 505); this.panel1.TabIndex = 0; // + // vScrollBar1 + // + this.vScrollBar1.Dock = System.Windows.Forms.DockStyle.Right; + this.vScrollBar1.Location = new System.Drawing.Point(360, 0); + this.vScrollBar1.Name = "vScrollBar1"; + this.vScrollBar1.Size = new System.Drawing.Size(17, 505); + this.vScrollBar1.TabIndex = 0; + // // VirtualFlowControl // this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.Controls.Add(this.vScrollBar1); this.Controls.Add(this.panel1); this.Name = "VirtualFlowControl"; this.Size = new System.Drawing.Size(377, 505); @@ -55,5 +65,6 @@ #endregion private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.VScrollBar vScrollBar1; } } diff --git a/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs b/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs index d031d191..296e34bf 100644 --- a/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs +++ b/Source/LibationWinForms/ProcessQueue/VirtualFlowControl.cs @@ -75,7 +75,6 @@ namespace LibationWinForms.ProcessQueue /// private readonly int TopMargin; - private readonly VScrollBar vScrollBar1; private readonly List BookControls = new(); #endregion @@ -101,16 +100,6 @@ namespace LibationWinForms.ProcessQueue { InitializeComponent(); - vScrollBar1 = new VScrollBar - { - Minimum = 0, - Value = 0, - Dock = DockStyle.Right - }; - Controls.Add(vScrollBar1); - - vScrollBar1.Scroll += (_, s) => SetScrollPosition(s.NewValue); - panel1.Width -= vScrollBar1.Width + panel1.Margin.Right; panel1.Resize += (_, _) => { AdjustScrollBar(); @@ -135,6 +124,7 @@ namespace LibationWinForms.ProcessQueue panel1.Controls.Add(control); } + vScrollBar1.Scroll += (_, s) => SetScrollPosition(s.NewValue); vScrollBar1.SmallChange = SmallScrollChange; panel1.Height += NUM_BLANK_SPACES_AT_BOTTOM * VirtualControlHeight; } @@ -227,6 +217,8 @@ namespace LibationWinForms.ProcessQueue /// private void SetScrollPosition(int value) { + if (!vScrollBar1.Enabled) return; + int newPos = (int)Math.Round((double)value / SmallScrollChange) * SmallScrollChange; if (vScrollBar1.Value != newPos) { From e8a320dac97e545767f5386c4b474bb47ddda497 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Sun, 22 May 2022 20:00:41 -0600 Subject: [PATCH 02/15] Add grid categories --- Source/AppScaffolding/AppScaffolding.csproj | 2 +- Source/DataLayer/EfClasses/Rating.cs | 2 +- .../Dialogs/RemoveBooksDialog.cs | 11 +- Source/LibationWinForms/Form1.cs | 4 +- .../LibationWinForms/LibationWinForms.csproj | 6 + .../Properties/Resources.Designer.cs | 20 ++ .../Properties/Resources.resx | 6 + Source/LibationWinForms/Resources/minus.png | Bin 0 -> 425 bytes Source/LibationWinForms/Resources/plus.png | Bin 0 -> 689 bytes .../grid/FilterableSortableBindingList.cs | 75 ++++- Source/LibationWinForms/grid/GridEntry.cs | 271 +++--------------- .../LiberateDataGridViewImageButtonColumn.cs | 22 +- .../LibationWinForms/grid/LibraryBookEntry.cs | 253 ++++++++++++++++ .../grid/MasterDataGridView.cs | 26 ++ .../grid/ProductsDisplay.Designer.cs | 46 +++ .../LibationWinForms/grid/ProductsDisplay.cs | 152 ++++++++++ .../grid/ProductsDisplay.resx | 63 ++++ .../grid/ProductsGrid.Designer.cs | 28 +- Source/LibationWinForms/grid/ProductsGrid.cs | 193 +++++++------ .../LibationWinForms/grid/ProductsGrid.resx | 6 - Source/LibationWinForms/grid/SeriesEntry.cs | 90 ++++++ .../grid/SortableBindingList1.cs | 111 +++++++ 22 files changed, 1008 insertions(+), 379 deletions(-) create mode 100644 Source/LibationWinForms/Resources/minus.png create mode 100644 Source/LibationWinForms/Resources/plus.png create mode 100644 Source/LibationWinForms/grid/LibraryBookEntry.cs create mode 100644 Source/LibationWinForms/grid/MasterDataGridView.cs create mode 100644 Source/LibationWinForms/grid/ProductsDisplay.Designer.cs create mode 100644 Source/LibationWinForms/grid/ProductsDisplay.cs create mode 100644 Source/LibationWinForms/grid/ProductsDisplay.resx create mode 100644 Source/LibationWinForms/grid/SeriesEntry.cs create mode 100644 Source/LibationWinForms/grid/SortableBindingList1.cs diff --git a/Source/AppScaffolding/AppScaffolding.csproj b/Source/AppScaffolding/AppScaffolding.csproj index 37d7e6ca..db37ad59 100644 --- a/Source/AppScaffolding/AppScaffolding.csproj +++ b/Source/AppScaffolding/AppScaffolding.csproj @@ -3,7 +3,7 @@ net6.0-windows - 7.7.1.1 + 7.7.0.14 diff --git a/Source/DataLayer/EfClasses/Rating.cs b/Source/DataLayer/EfClasses/Rating.cs index 761633be..18b0cd05 100644 --- a/Source/DataLayer/EfClasses/Rating.cs +++ b/Source/DataLayer/EfClasses/Rating.cs @@ -12,7 +12,7 @@ namespace DataLayer public float StoryRating { get; private set; } private Rating() { } - internal Rating(float overallRating, float performanceRating, float storyRating) + public Rating(float overallRating, float performanceRating, float storyRating) { OverallRating = overallRating; PerformanceRating = performanceRating; diff --git a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs b/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs index ecd56188..bb1759dc 100644 --- a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs +++ b/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs @@ -121,10 +121,8 @@ namespace LibationWinForms.Dialogs } } - internal class RemovableGridEntry : GridEntry + internal class RemovableGridEntry : LibraryBookEntry { - private static readonly IComparer BoolComparer = new ObjectComparer(); - private bool _remove = false; public RemovableGridEntry(LibraryBook libraryBook) : base(libraryBook) { } @@ -147,12 +145,5 @@ namespace LibationWinForms.Dialogs return Remove; return base.GetMemberValue(memberName); } - - public override IComparer GetMemberComparer(Type memberType) - { - if (memberType == typeof(bool)) - return BoolComparer; - return base.GetMemberComparer(memberType); - } } } diff --git a/Source/LibationWinForms/Form1.cs b/Source/LibationWinForms/Form1.cs index 04150167..0c2f3ce1 100644 --- a/Source/LibationWinForms/Form1.cs +++ b/Source/LibationWinForms/Form1.cs @@ -12,7 +12,7 @@ namespace LibationWinForms { public partial class Form1 : Form { - private ProductsGrid productsGrid { get; } + private ProductsDisplay productsGrid { get; } public Form1() { @@ -26,7 +26,7 @@ namespace LibationWinForms // Failed to create component 'ProductsGrid'. The error message follows: // 'Microsoft.DotNet.DesignTools.Client.DesignToolsServerException: Object reference not set to an instance of an object. // Since the designer's choking on it, I'm keeping it below the DesignMode check to be safe - productsGrid = new ProductsGrid { Dock = DockStyle.Fill }; + productsGrid = new ProductsDisplay { Dock = DockStyle.Fill }; gridPanel.Controls.Add(productsGrid); } diff --git a/Source/LibationWinForms/LibationWinForms.csproj b/Source/LibationWinForms/LibationWinForms.csproj index 2ecd019b..979db0ff 100644 --- a/Source/LibationWinForms/LibationWinForms.csproj +++ b/Source/LibationWinForms/LibationWinForms.csproj @@ -45,6 +45,9 @@ + + UserControl + True True @@ -53,6 +56,9 @@ + + Designer + ResXFileCodeGenerator Resources.Designer.cs diff --git a/Source/LibationWinForms/Properties/Resources.Designer.cs b/Source/LibationWinForms/Properties/Resources.Designer.cs index 468d9e4d..f3e881e2 100644 --- a/Source/LibationWinForms/Properties/Resources.Designer.cs +++ b/Source/LibationWinForms/Properties/Resources.Designer.cs @@ -229,5 +229,25 @@ namespace LibationWinForms.Properties { return ((System.Drawing.Bitmap)(obj)); } } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap minus { + get { + object obj = ResourceManager.GetObject("minus", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap plus { + get { + object obj = ResourceManager.GetObject("plus", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } } } diff --git a/Source/LibationWinForms/Properties/Resources.resx b/Source/LibationWinForms/Properties/Resources.resx index 0a1ae67a..95b3b8a0 100644 --- a/Source/LibationWinForms/Properties/Resources.resx +++ b/Source/LibationWinForms/Properties/Resources.resx @@ -169,4 +169,10 @@ ..\Resources\liberate_yellow_pdf_yes.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\minus.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\plus.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + \ No newline at end of file diff --git a/Source/LibationWinForms/Resources/minus.png b/Source/LibationWinForms/Resources/minus.png new file mode 100644 index 0000000000000000000000000000000000000000..bfc2cf4abc65ecbd5ac663ccf26584d356818814 GIT binary patch literal 425 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=oCO|{#S9GG!XV7ZFl&wkP*5S+ zBgmJ5p-Pp3p`n?9;pcxK{gQ#9)PRBERRRNp)eHs(@%%~gN8K1081+3}978H@y}4=V zci2I|;o|w$z5E2+II)LiOI_s3muj?ww!P_=H)F*;mrLYpJ|UCeg?qu<@w|S{tuv+bcHU-fW7@+c{XpA$&9{y-rIUB^ zzhmMpsCStDK&*m!Uc>8kWjB^{Z#m%XSC^5yK{keQ@qd9&LLzv*An>66$ajXYck|g# T{9jWB3}yyTS3j3^P6Z9gktZjYPrOP?j1FFJAGKiK|}SEq(q{#f~j35QNyI>IO9)6v_Ms3UX2lW&J@ zzw5U&A*PtNH}|%0-fV3D+@(b;y0>dKqX7fk0S1W%t>|RgW~=A#8pJNJv^j*kth;VB zeM0Mm;&);@82TDxV;l4n81^vqD-{VBv&>;$eqi6aeatL3n9IHt&#`nkyl<`fe%>pk ze|FW~XTSAIJXI(s^M-%eKXLiJ`&QQlGG#D%9y_~SQRS7mDnt0D`Bxc1Rx&UrFz_@0 z?f);=P<(E+P{{umN(v_p<9z=Vf$2@a>Anae6tJ%OKsja!;tdv%j$+VniLwli{?fd#|k4rt& zT>tbYQ>0PY#jqPs&oFWxePWnjxptcJo6RXY_osip!W?t3aL?1NsgXYoM87$`pb8Yut( literal 0 HcmV?d00001 diff --git a/Source/LibationWinForms/grid/FilterableSortableBindingList.cs b/Source/LibationWinForms/grid/FilterableSortableBindingList.cs index 1e896e0b..e3bcaad0 100644 --- a/Source/LibationWinForms/grid/FilterableSortableBindingList.cs +++ b/Source/LibationWinForms/grid/FilterableSortableBindingList.cs @@ -19,14 +19,16 @@ 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 : SortableBindingList, IBindingListView + internal class FilterableSortableBindingList : SortableBindingList1, IBindingListView { /// /// 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); } @@ -48,7 +50,14 @@ namespace LibationWinForms } /// All items in the list, including those filtered out. - public List AllItems() => Items.Concat(FilterRemoved).ToList(); + public List AllItems() + { + var allItems = Items.Concat(FilterRemoved); + + var series = allItems.Where(i => i is SeriesEntry).Cast().SelectMany(s => s.Children); + + return series.Concat(allItems).ToList(); + } private void ApplyFilter(string filterString) { @@ -57,18 +66,49 @@ namespace LibationWinForms FilterString = filterString; - var searchResults = SearchEngineCommands.Search(filterString); - var filteredOut = Items.ExceptBy(searchResults.Docs.Select(d => d.ProductId), ge => ge.AudibleProductId); + SearchResults = SearchEngineCommands.Search(filterString); + var filteredOut = Items.Where(i => i is LibraryBookEntry).Cast().ExceptBy(SearchResults.Docs.Select(d => d.ProductId), ge => ge.AudibleProductId).Cast().ToList(); - for (int i = Items.Count - 1; i >= 0; i--) + var parents = Items.Where(i => i is SeriesEntry).Cast(); + + foreach (var p in parents) { - if (filteredOut.Contains(Items[i])) + if (p.Children.Cast().ExceptBy(SearchResults.Docs.Select(d => d.ProductId), ge => ge.AudibleProductId).Count() == p.Children.Count) { - FilterRemoved.Add(Items[i]); - Items.RemoveAt(i); - base.OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, i)); + //Don't show series whose episodes have all been filtered out + filteredOut.Add(p); } } + + for (int i = 0; i < filteredOut.Count; i++) + { + FilterRemoved.Add(filteredOut[i]); + base.Remove(filteredOut[i]); + } + } + + public void CollapseItem(SeriesEntry sEntry) + { + foreach (var item in Items.Where(b => b is LibraryBookEntry).Cast().Where(b => b.Parent == sEntry).ToList()) + base.Remove(item); + + sEntry.Liberate.Expanded = false; + } + + public void ExpandItem(SeriesEntry sEntry) + { + var sindex = Items.IndexOf(sEntry); + var children = sEntry.Children.Cast().ToList(); + for (int i = 0; i < children.Count; i++) + { + if (SearchResults is null || SearchResults.Docs.Any(d=> d.ProductId == children[i].AudibleProductId)) + Insert(++sindex, children[i]); + else + { + FilterRemoved.Add(children[i]); + } + } + sEntry.Liberate.Expanded = true; } public void RemoveFilter() @@ -77,18 +117,27 @@ namespace LibationWinForms int visibleCount = Items.Count; for (int i = 0; i < FilterRemoved.Count; i++) - base.InsertItem(i + visibleCount, FilterRemoved[i]); - OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); + { + if (FilterRemoved[i].Parent is null || FilterRemoved[i].Parent.Liberate.Expanded) + base.InsertItem(i + visibleCount, FilterRemoved[i]); + } FilterRemoved.Clear(); if (IsSortedCore) Sort(); else - //No user-defined sort is applied, so do default sorting by date added, descending - ((List)Items).Sort((i1, i2) => i2.LibraryBook.DateAdded.CompareTo(i1.LibraryBook.DateAdded)); + //No user sort is applied, so do default sorting by PurchaseDate, descending + { + Comparer.PropertyName = nameof(GridEntry.DateAdded); + Comparer.Direction = ListSortDirection.Descending; + Sort(); + } + + OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); FilterString = null; + SearchResults = null; } } } diff --git a/Source/LibationWinForms/grid/GridEntry.cs b/Source/LibationWinForms/grid/GridEntry.cs index 3e02dc93..a9d86b41 100644 --- a/Source/LibationWinForms/grid/GridEntry.cs +++ b/Source/LibationWinForms/grid/GridEntry.cs @@ -1,101 +1,65 @@ -using System; +using DataLayer; +using Dinah.Core.DataBinding; +using Dinah.Core.Drawing; +using LibationFileManager; +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 { - /// - /// The View Model for a LibraryBook - /// - internal class GridEntry : AsyncNotifyPropertyChanged, IMemberComparable + public interface IHierarchical where T : class { - #region implementation properties NOT exposed to the view - // hide from public fields from Data Source GUI with [Browsable(false)] + T Parent { get; } + List Children { get; } + } + internal class LiberateStatus + { + public LiberatedStatus BookStatus; + public LiberatedStatus? PdfStatus; + public bool IsSeries; + public bool Expanded; + } - [Browsable(false)] - public string AudibleProductId => Book.AudibleProductId; - [Browsable(false)] - public LibraryBook LibraryBook { get; private set; } - [Browsable(false)] - public string LongDescription { get; private set; } - #endregion + internal abstract class GridEntry : AsyncNotifyPropertyChanged, IMemberComparable, IHierarchical + { + protected abstract Book Book { get; } - #region Model properties exposed to the view private Image _cover; - - private DateTime lastStatusUpdate = default; - private LiberatedStatus _bookStatus; - private LiberatedStatus? _pdfStatus; + #region Model properties exposed to the view public Image Cover { get => _cover; - private set + protected set { _cover = value; NotifyPropertyChanged(); } } - - public string ProductRating { get; private set; } - public string PurchaseDate { get; private set; } - public string MyRating { get; private set; } - public string Series { get; private set; } - public string Title { get; private set; } - public string Length { get; private set; } - public string Authors { get; private set; } - public string Narrators { get; private set; } - public string Category { get; private set; } - public string Misc { get; private set; } - public string Description { get; private set; } - public string DisplayTags => string.Join("\r\n", Book.UserDefinedItem.TagsEnumerated); - - // these 2 values being in 1 field is the trick behind getting the liberated+pdf 'stoplight' icon to draw. See: LiberateDataGridViewImageButtonCell.Paint - public (LiberatedStatus BookStatus, LiberatedStatus? PdfStatus) Liberate - { - get - { - //Cache these statuses for faster sorting. - if ((DateTime.Now - lastStatusUpdate).TotalSeconds > 2) - { - _bookStatus = LibraryCommands.Liberated_Status(LibraryBook.Book); - _pdfStatus = LibraryCommands.Pdf_Status(LibraryBook.Book); - lastStatusUpdate = DateTime.Now; - } - return (_bookStatus, _pdfStatus); - } - } + public GridEntry Parent { get; set; } + public List Children { get; set; } + public abstract string ProductRating { get; protected set; } + public abstract string PurchaseDate { get; protected set; } + public abstract DateTime DateAdded { get; } + public abstract string MyRating { get; protected set; } + public abstract string Series { get; protected set; } + public abstract string Title { get; protected set; } + public abstract string Length { get; protected set; } + public abstract string Authors { get; protected set; } + public abstract string Narrators { get; protected set; } + public abstract string Category { get; protected set; } + public abstract string Misc { get; protected set; } + public abstract string Description { get; protected set; } + public abstract string DisplayTags { get; } + public abstract LiberateStatus Liberate { get; } + public abstract object GetMemberValue(string memberName); #endregion + public IComparer GetMemberComparer(Type memberType) => _memberTypeComparers[memberType]; - // alias - private Book Book => LibraryBook.Book; - - public GridEntry(LibraryBook libraryBook) => setLibraryBook(libraryBook); - - public void UpdateLibraryBook(LibraryBook libraryBook) + protected void LoadCover() { - if (AudibleProductId != libraryBook.Book.AudibleProductId) - throw new Exception("Invalid grid entry update. IDs must match"); - - setLibraryBook(libraryBook); - - NotifyPropertyChanged(); - } - - private void setLibraryBook(LibraryBook libraryBook) - { - LibraryBook = libraryBook; - _memberValues = CreateMemberValueDictionary(); - // Get cover art. If it's default, subscribe to PictureCached { (bool isDefault, byte[] picture) = PictureStorage.GetPicture(new PictureDefinition(Book.PictureId, PictureSize._80x80)); @@ -106,24 +70,6 @@ namespace LibationWinForms // Mutable property. Set the field so PropertyChanged isn't fired. _cover = ImageReader.ToImage(picture); } - - // Immutable properties - { - Title = Book.Title; - Series = Book.SeriesNames(); - Length = Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min"; - MyRating = Book.UserDefinedItem.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace(""); - PurchaseDate = libraryBook.DateAdded.ToString("d"); - ProductRating = Book.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace(""); - Authors = Book.AuthorNames(); - Narrators = Book.NarratorNames(); - Category = string.Join(" > ", Book.CategoriesNames()); - Misc = GetMiscDisplay(libraryBook); - LongDescription = GetDescriptionDisplay(Book); - Description = TrimTextToWord(LongDescription, 62); - } - - UserDefinedItem.ItemChanged += UserDefinedItem_ItemChanged; } private void PictureStorage_PictureCached(object sender, PictureCachedEventArgs e) @@ -135,154 +81,19 @@ namespace LibationWinForms } } - #region detect changes to the model, update the view, and save to database. - - /// - /// This event handler receives notifications from the model that it has changed. - /// Save to the database and notify the view that it's changed. - /// - private void UserDefinedItem_ItemChanged(object sender, string itemName) - { - var udi = sender as UserDefinedItem; - - if (udi.Book.AudibleProductId != Book.AudibleProductId) - return; - - switch (itemName) - { - case nameof(udi.Tags): - Book.UserDefinedItem.Tags = udi.Tags; - NotifyPropertyChanged(nameof(DisplayTags)); - break; - case nameof(udi.BookStatus): - Book.UserDefinedItem.BookStatus = udi.BookStatus; - _bookStatus = udi.BookStatus; - NotifyPropertyChanged(nameof(Liberate)); - break; - case nameof(udi.PdfStatus): - Book.UserDefinedItem.PdfStatus = udi.PdfStatus; - _pdfStatus = udi.PdfStatus; - NotifyPropertyChanged(nameof(Liberate)); - break; - } - } - - /// Save edits to the database - public void Commit(string newTags, LiberatedStatus bookStatus, LiberatedStatus? pdfStatus) - { - // validate - if (DisplayTags.EqualsInsensitive(newTags) && - Liberate.BookStatus == bookStatus && - Liberate.PdfStatus == pdfStatus) - return; - - // update cache - _bookStatus = bookStatus; - _pdfStatus = pdfStatus; - - // set + save - Book.UserDefinedItem.Tags = newTags; - Book.UserDefinedItem.BookStatus = bookStatus; - Book.UserDefinedItem.PdfStatus = pdfStatus; - LibraryCommands.UpdateUserDefinedItem(Book); - } - - #endregion - - #region Data Sorting - // These methods are implementation of Dinah.Core.DataBinding.IMemberComparable - // Used by Dinah.Core.DataBinding.SortableBindingList for all sorting - public virtual object GetMemberValue(string memberName) => _memberValues[memberName](); - public virtual IComparer GetMemberComparer(Type memberType) => _memberTypeComparers[memberType]; - - private Dictionary> _memberValues { get; set; } - - /// - /// Create getters for all member object values by name - /// - private Dictionary> CreateMemberValueDictionary() => new() - { - { nameof(Title), () => Book.TitleSortable() }, - { nameof(Series), () => Book.SeriesSortable() }, - { nameof(Length), () => Book.LengthInMinutes }, - { nameof(MyRating), () => Book.UserDefinedItem.Rating.FirstScore() }, - { nameof(PurchaseDate), () => LibraryBook.DateAdded }, - { nameof(ProductRating), () => Book.Rating.FirstScore() }, - { nameof(Authors), () => Authors }, - { nameof(Narrators), () => Narrators }, - { nameof(Description), () => Description }, - { nameof(Category), () => Category }, - { nameof(Misc), () => Misc }, - { nameof(DisplayTags), () => DisplayTags }, - { nameof(Liberate), () => Liberate.BookStatus } - }; - // Instantiate comparers for every exposed member object type. private static readonly Dictionary _memberTypeComparers = new() { { typeof(string), new ObjectComparer() }, { typeof(int), new ObjectComparer() }, { typeof(float), new ObjectComparer() }, + { typeof(bool), new ObjectComparer() }, { typeof(DateTime), new ObjectComparer() }, { typeof(LiberatedStatus), new ObjectComparer() }, }; - #endregion - - #region Static library display functions - - /// - /// This information should not change during lifetime, so call only once. - /// - private static string GetDescriptionDisplay(Book book) - { - var doc = new HtmlAgilityPack.HtmlDocument(); - doc.LoadHtml(book?.Description?.Replace("

", "\r\n\r\n

") ?? ""); - return doc.DocumentNode.InnerText.Trim(); - } - - private static string TrimTextToWord(string text, int maxLength) - { - return - text.Length <= maxLength ? - text : - text.Substring(0, maxLength - 3) + "..."; - } - - /// - /// This information should not change during lifetime, so call only once. - /// Maximum of 5 text rows will fit in 80-pixel row height. - /// - private static string GetMiscDisplay(LibraryBook libraryBook) - { - var details = new List(); - - var locale = libraryBook.Book.Locale.DefaultIfNullOrWhiteSpace("[unknown]"); - var acct = libraryBook.Account.DefaultIfNullOrWhiteSpace("[unknown]"); - - details.Add($"Account: {locale} - {acct}"); - - if (libraryBook.Book.HasPdf()) - details.Add("Has PDF"); - if (libraryBook.Book.IsAbridged) - details.Add("Abridged"); - if (libraryBook.Book.DatePublished.HasValue) - details.Add($"Date pub'd: {libraryBook.Book.DatePublished.Value:MM/dd/yyyy}"); - // this goes last since it's most likely to have a line-break - if (!string.IsNullOrWhiteSpace(libraryBook.Book.Publisher)) - details.Add($"Pub: {libraryBook.Book.Publisher.Trim()}"); - - if (!details.Any()) - return "[details not imported]"; - - return string.Join("\r\n", details); - } - - #endregion - ~GridEntry() { - UserDefinedItem.ItemChanged -= UserDefinedItem_ItemChanged; PictureStorage.PictureCached -= PictureStorage_PictureCached; } } diff --git a/Source/LibationWinForms/grid/LiberateDataGridViewImageButtonColumn.cs b/Source/LibationWinForms/grid/LiberateDataGridViewImageButtonColumn.cs index e5ab82b8..159b1e4a 100644 --- a/Source/LibationWinForms/grid/LiberateDataGridViewImageButtonColumn.cs +++ b/Source/LibationWinForms/grid/LiberateDataGridViewImageButtonColumn.cs @@ -20,15 +20,27 @@ namespace LibationWinForms { base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, paintParts); - if (value is (LiberatedStatus, LiberatedStatus) or (LiberatedStatus, null)) + if (value is LiberateStatus status) { - var (bookState, pdfState) = ((LiberatedStatus bookState, LiberatedStatus? pdfState))value; + if (status.IsSeries) + { + var imageName = status.Expanded ? "minus" : "plus"; + var text = status.Expanded ? "Click to Collpase" : "Click to Expand"; - (string mouseoverText, Bitmap buttonImage) = GetLiberateDisplay(bookState, pdfState); + var bmp = (Bitmap)Properties.Resources.ResourceManager.GetObject(imageName); + DrawButtonImage(graphics, bmp, cellBounds); - DrawButtonImage(graphics, buttonImage, cellBounds); + ToolTipText = text; - ToolTipText = mouseoverText; + } + else + { + (string mouseoverText, Bitmap buttonImage) = GetLiberateDisplay(status.BookStatus, status.PdfStatus); + + DrawButtonImage(graphics, buttonImage, cellBounds); + + ToolTipText = mouseoverText; + } } } diff --git a/Source/LibationWinForms/grid/LibraryBookEntry.cs b/Source/LibationWinForms/grid/LibraryBookEntry.cs new file mode 100644 index 00000000..e87ae630 --- /dev/null +++ b/Source/LibationWinForms/grid/LibraryBookEntry.cs @@ -0,0 +1,253 @@ +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 +{ + /// + /// The View Model for a LibraryBook + /// + internal class LibraryBookEntry : GridEntry + { + #region implementation properties NOT exposed to the view + // hide from public fields from Data Source GUI with [Browsable(false)] + + [Browsable(false)] + public string AudibleProductId => Book.AudibleProductId; + [Browsable(false)] + public LibraryBook LibraryBook { get; private set; } + [Browsable(false)] + public string LongDescription { get; private set; } + #endregion + + // alias + protected override Book Book => LibraryBook.Book; + #region Model properties exposed to the view + + private DateTime lastStatusUpdate = default; + private LiberatedStatus _bookStatus; + private LiberatedStatus? _pdfStatus; + + public override DateTime DateAdded => LibraryBook.DateAdded; + public override string ProductRating { get; protected set; } + public override string PurchaseDate { get; protected set; } + public override string MyRating { get; protected set; } + public override string Series { get; protected set; } + public override string Title { get; protected set; } + public override string Length { get; protected set; } + public override string Authors { get; protected set; } + public override string Narrators { get; protected set; } + public override string Category { get; protected set; } + public override string Misc { get; protected set; } + public override string Description { get; protected set; } + public override string DisplayTags => string.Join("\r\n", Book.UserDefinedItem.TagsEnumerated); + + // these 2 values being in 1 field is the trick behind getting the liberated+pdf 'stoplight' icon to draw. See: LiberateDataGridViewImageButtonCell.Paint + public override LiberateStatus Liberate + { + get + { + //Cache these statuses for faster sorting. + if ((DateTime.Now - lastStatusUpdate).TotalSeconds > 2) + { + _bookStatus = LibraryCommands.Liberated_Status(LibraryBook.Book); + _pdfStatus = LibraryCommands.Pdf_Status(LibraryBook.Book); + lastStatusUpdate = DateTime.Now; + } + return new LiberateStatus { BookStatus = _bookStatus, PdfStatus = _pdfStatus, IsSeries = false }; + } + } + #endregion + + + public LibraryBookEntry(LibraryBook libraryBook) => setLibraryBook(libraryBook); + + public void UpdateLibraryBook(LibraryBook libraryBook) + { + if (AudibleProductId != libraryBook.Book.AudibleProductId) + throw new Exception("Invalid grid entry update. IDs must match"); + + setLibraryBook(libraryBook); + + NotifyPropertyChanged(); + } + + private void setLibraryBook(LibraryBook libraryBook) + { + LibraryBook = libraryBook; + _memberValues = CreateMemberValueDictionary(); + + LoadCover(); + + // Immutable properties + { + Title = Book.Title; + Series = Book.SeriesNames(); + Length = Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min"; + MyRating = Book.UserDefinedItem.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace(""); + PurchaseDate = libraryBook.DateAdded.ToString("d"); + ProductRating = Book.Rating?.ToStarString()?.DefaultIfNullOrWhiteSpace(""); + Authors = Book.AuthorNames(); + Narrators = Book.NarratorNames(); + Category = string.Join(" > ", Book.CategoriesNames()); + Misc = GetMiscDisplay(libraryBook); + LongDescription = GetDescriptionDisplay(Book); + Description = TrimTextToWord(LongDescription, 62); + } + + UserDefinedItem.ItemChanged += UserDefinedItem_ItemChanged; + } + + + #region detect changes to the model, update the view, and save to database. + + /// + /// This event handler receives notifications from the model that it has changed. + /// Save to the database and notify the view that it's changed. + /// + private void UserDefinedItem_ItemChanged(object sender, string itemName) + { + var udi = sender as UserDefinedItem; + + if (udi.Book.AudibleProductId != Book.AudibleProductId) + return; + + switch (itemName) + { + case nameof(udi.Tags): + Book.UserDefinedItem.Tags = udi.Tags; + NotifyPropertyChanged(nameof(DisplayTags)); + break; + case nameof(udi.BookStatus): + Book.UserDefinedItem.BookStatus = udi.BookStatus; + _bookStatus = udi.BookStatus; + NotifyPropertyChanged(nameof(Liberate)); + break; + case nameof(udi.PdfStatus): + Book.UserDefinedItem.PdfStatus = udi.PdfStatus; + _pdfStatus = udi.PdfStatus; + NotifyPropertyChanged(nameof(Liberate)); + break; + } + } + + /// Save edits to the database + public void Commit(string newTags, LiberatedStatus bookStatus, LiberatedStatus? pdfStatus) + { + // validate + if (DisplayTags.EqualsInsensitive(newTags) && + Liberate.BookStatus == bookStatus && + Liberate.PdfStatus == pdfStatus) + return; + + // update cache + _bookStatus = bookStatus; + _pdfStatus = pdfStatus; + + // set + save + Book.UserDefinedItem.Tags = newTags; + Book.UserDefinedItem.BookStatus = bookStatus; + Book.UserDefinedItem.PdfStatus = pdfStatus; + LibraryCommands.UpdateUserDefinedItem(Book); + } + + #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() + { + { nameof(Title), () => Book.TitleSortable() }, + { nameof(Series), () => Book.SeriesSortable() }, + { nameof(Length), () => Book.LengthInMinutes }, + { nameof(MyRating), () => Book.UserDefinedItem.Rating.FirstScore() }, + { nameof(PurchaseDate), () => LibraryBook.DateAdded }, + { nameof(ProductRating), () => Book.Rating.FirstScore() }, + { nameof(Authors), () => Authors }, + { nameof(Narrators), () => Narrators }, + { nameof(Description), () => Description }, + { nameof(Category), () => Category }, + { nameof(Misc), () => Misc }, + { nameof(DisplayTags), () => DisplayTags }, + { nameof(Liberate), () => Liberate.BookStatus }, + { nameof(DateAdded), () => DateAdded }, + }; + + + #endregion + + #region Static library display functions + + /// + /// This information should not change during lifetime, so call only once. + /// + private static string GetDescriptionDisplay(Book book) + { + var doc = new HtmlAgilityPack.HtmlDocument(); + doc.LoadHtml(book?.Description?.Replace("

", "\r\n\r\n

") ?? ""); + return doc.DocumentNode.InnerText.Trim(); + } + + private static string TrimTextToWord(string text, int maxLength) + { + return + text.Length <= maxLength ? + text : + text.Substring(0, maxLength - 3) + "..."; + } + + /// + /// This information should not change during lifetime, so call only once. + /// Maximum of 5 text rows will fit in 80-pixel row height. + /// + private static string GetMiscDisplay(LibraryBook libraryBook) + { + var details = new List(); + + var locale = libraryBook.Book.Locale.DefaultIfNullOrWhiteSpace("[unknown]"); + var acct = libraryBook.Account.DefaultIfNullOrWhiteSpace("[unknown]"); + + details.Add($"Account: {locale} - {acct}"); + + if (libraryBook.Book.HasPdf()) + details.Add("Has PDF"); + if (libraryBook.Book.IsAbridged) + details.Add("Abridged"); + if (libraryBook.Book.DatePublished.HasValue) + details.Add($"Date pub'd: {libraryBook.Book.DatePublished.Value:MM/dd/yyyy}"); + // this goes last since it's most likely to have a line-break + if (!string.IsNullOrWhiteSpace(libraryBook.Book.Publisher)) + details.Add($"Pub: {libraryBook.Book.Publisher.Trim()}"); + + if (!details.Any()) + return "[details not imported]"; + + return string.Join("\r\n", details); + } + + #endregion + + ~LibraryBookEntry() + { + UserDefinedItem.ItemChanged -= UserDefinedItem_ItemChanged; + } + } +} diff --git a/Source/LibationWinForms/grid/MasterDataGridView.cs b/Source/LibationWinForms/grid/MasterDataGridView.cs new file mode 100644 index 00000000..fd3b481a --- /dev/null +++ b/Source/LibationWinForms/grid/MasterDataGridView.cs @@ -0,0 +1,26 @@ +using Dinah.Core.Windows.Forms; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace LibationWinForms +{ + + internal class MasterDataGridView : DataGridView + { + internal delegate void LibraryBookEntryClickedEventHandler(DataGridViewCellEventArgs e, LibraryBookEntry entry); + public event LibraryBookEntryClickedEventHandler LibraryBookEntryClicked; + public MasterDataGridView() + { + + } + + + public GridEntry getGridEntry(int rowIndex) => this.GetBoundItem(rowIndex); + + } +} diff --git a/Source/LibationWinForms/grid/ProductsDisplay.Designer.cs b/Source/LibationWinForms/grid/ProductsDisplay.Designer.cs new file mode 100644 index 00000000..d15ad85c --- /dev/null +++ b/Source/LibationWinForms/grid/ProductsDisplay.Designer.cs @@ -0,0 +1,46 @@ +namespace LibationWinForms +{ + partial class ProductsDisplay + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.SuspendLayout(); + // + // ProductsDisplay + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + this.Name = "ProductsDisplay"; + this.Size = new System.Drawing.Size(1510, 380); + this.ResumeLayout(false); + + } + + #endregion + } +} diff --git a/Source/LibationWinForms/grid/ProductsDisplay.cs b/Source/LibationWinForms/grid/ProductsDisplay.cs new file mode 100644 index 00000000..e0e0fd58 --- /dev/null +++ b/Source/LibationWinForms/grid/ProductsDisplay.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; +using ApplicationServices; +using DataLayer; +using Dinah.Core.Windows.Forms; +using FileLiberator; +using LibationFileManager; +using LibationWinForms.Dialogs; + +namespace LibationWinForms +{ + + #region // legacy instructions to update data_grid_view + // INSTRUCTIONS TO UPDATE DATA_GRID_VIEW + // - delete current DataGridView + // - view > other windows > data sources + // - refresh + // OR + // - Add New Data Source + // Object. Next + // LibationWinForms + // AudibleDTO + // GridEntry + // - go to Design view + // - click on Data Sources > ProductItem. dropdown: DataGridView + // - drag/drop ProductItem on design surface + // + // as of august 2021 this does not work in vs2019 with .net5 projects + // VS has improved since then with .net6+ but I haven't checked again + #endregion + + + public partial class ProductsDisplay : UserControl + { + public event EventHandler LiberateClicked; + /// Number of visible rows has changed + public event EventHandler VisibleCountChanged; + + // alias + + private ProductsGrid grid; + + public ProductsDisplay() + { + InitializeComponent(); + + grid = new ProductsGrid(); + grid.Dock = DockStyle.Fill; + Controls.Add(grid); + + if (this.DesignMode) + return; + + grid.LiberateClicked += (_, book) => LiberateClicked?.Invoke(this, book.LibraryBook); + grid.DetailsClicked += Grid_DetailsClicked; + grid.CoverClicked += Grid_CoverClicked; + grid.DescriptionClicked += Grid_DescriptionClicked1; + } + + #region Button controls + + private ImageDisplay imageDisplay; + private async void Grid_CoverClicked(DataGridViewCellEventArgs e, LibraryBookEntry liveGridEntry) + { + var picDefinition = new PictureDefinition(liveGridEntry.LibraryBook.Book.PictureLarge ?? liveGridEntry.LibraryBook.Book.PictureId, PictureSize.Native); + var picDlTask = Task.Run(() => PictureStorage.GetPictureSynchronously(picDefinition)); + + (_, byte[] initialImageBts) = PictureStorage.GetPicture(new PictureDefinition(liveGridEntry.LibraryBook.Book.PictureId, PictureSize._80x80)); + var windowTitle = $"{liveGridEntry.Title} - Cover"; + + if (imageDisplay is null || imageDisplay.IsDisposed || !imageDisplay.Visible) + { + imageDisplay = new ImageDisplay(); + imageDisplay.RestoreSizeAndLocation(Configuration.Instance); + imageDisplay.FormClosed += (_, _) => imageDisplay.SaveSizeAndLocation(Configuration.Instance); + imageDisplay.Show(this); + } + + imageDisplay.BookSaveDirectory = AudibleFileStorage.Audio.GetDestinationDirectory(liveGridEntry.LibraryBook); + imageDisplay.PictureFileName = System.IO.Path.GetFileName(AudibleFileStorage.Audio.GetBooksDirectoryFilename(liveGridEntry.LibraryBook, ".jpg")); + imageDisplay.Text = windowTitle; + imageDisplay.CoverPicture = initialImageBts; + imageDisplay.CoverPicture = await picDlTask; + } + + private void Grid_DescriptionClicked1(DataGridViewCellEventArgs e, LibraryBookEntry liveGridEntry, Rectangle cellRectangle) + { + var displayWindow = new DescriptionDisplay + { + SpawnLocation = PointToScreen(cellRectangle.Location + new Size(cellRectangle.Width, 0)), + DescriptionText = liveGridEntry.LongDescription, + BorderThickness = 2, + }; + + void CloseWindow(object o, EventArgs e) + { + displayWindow.Close(); + } + + grid.Scroll += CloseWindow; + displayWindow.FormClosed += (_, _) => grid.Scroll -= CloseWindow; + displayWindow.Show(this); + } + + + private void Grid_DetailsClicked(DataGridViewCellEventArgs e, LibraryBookEntry liveGridEntry) + { + var bookDetailsForm = new BookDetailsDialog(liveGridEntry.LibraryBook); + if (bookDetailsForm.ShowDialog() == DialogResult.OK) + liveGridEntry.Commit(bookDetailsForm.NewTags, bookDetailsForm.BookLiberatedStatus, bookDetailsForm.PdfLiberatedStatus); + } + + #endregion + + #region UI display functions + + private bool hasBeenDisplayed; + public event EventHandler InitialLoaded; + public void Display() + { + // don't return early if lib size == 0. this will not update correctly if all books are removed + var lib = DbContexts.GetLibrary_Flat_NoTracking(); + + if (!hasBeenDisplayed) + { + // bind + grid.bindToGrid(lib); + hasBeenDisplayed = true; + InitialLoaded?.Invoke(this, new()); + VisibleCountChanged?.Invoke(this, grid.GetVisible().Count()); + } + else + grid.updateGrid(lib); + + } + + #endregion + + #region Filter + + public void Filter(string searchString) + => grid.Filter(searchString); + + #endregion + + internal List GetVisible() => grid.GetVisible().ToList(); + } +} diff --git a/Source/LibationWinForms/grid/ProductsDisplay.resx b/Source/LibationWinForms/grid/ProductsDisplay.resx new file mode 100644 index 00000000..be5db7af --- /dev/null +++ b/Source/LibationWinForms/grid/ProductsDisplay.resx @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 81 + + \ No newline at end of file diff --git a/Source/LibationWinForms/grid/ProductsGrid.Designer.cs b/Source/LibationWinForms/grid/ProductsGrid.Designer.cs index b3eaecd5..27496d79 100644 --- a/Source/LibationWinForms/grid/ProductsGrid.Designer.cs +++ b/Source/LibationWinForms/grid/ProductsGrid.Designer.cs @@ -64,20 +64,20 @@ this.gridEntryDataGridView.AutoGenerateColumns = false; this.gridEntryDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.gridEntryDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { - this.liberateGVColumn, - this.coverGVColumn, - this.titleGVColumn, - this.authorsGVColumn, - this.narratorsGVColumn, - this.lengthGVColumn, - this.seriesGVColumn, - this.descriptionGVColumn, - this.categoryGVColumn, - this.productRatingGVColumn, - this.purchaseDateGVColumn, - this.myRatingGVColumn, - this.miscGVColumn, - this.tagAndDetailsGVColumn}); + this.liberateGVColumn, + this.coverGVColumn, + this.titleGVColumn, + this.authorsGVColumn, + this.narratorsGVColumn, + this.lengthGVColumn, + this.seriesGVColumn, + this.descriptionGVColumn, + this.categoryGVColumn, + this.productRatingGVColumn, + this.purchaseDateGVColumn, + this.myRatingGVColumn, + this.miscGVColumn, + this.tagAndDetailsGVColumn}); this.gridEntryDataGridView.ContextMenuStrip = this.contextMenuStrip1; this.gridEntryDataGridView.DataSource = this.gridEntryBindingSource; dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; diff --git a/Source/LibationWinForms/grid/ProductsGrid.cs b/Source/LibationWinForms/grid/ProductsGrid.cs index 690cb00d..2d585f27 100644 --- a/Source/LibationWinForms/grid/ProductsGrid.cs +++ b/Source/LibationWinForms/grid/ProductsGrid.cs @@ -2,14 +2,11 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; -using System.Threading.Tasks; using System.Windows.Forms; using ApplicationServices; using DataLayer; using Dinah.Core.Windows.Forms; -using FileLiberator; using LibationFileManager; -using LibationWinForms.Dialogs; namespace LibationWinForms { @@ -36,7 +33,17 @@ namespace LibationWinForms public partial class ProductsGrid : UserControl { - public event EventHandler LiberateClicked; + + internal delegate void LibraryBookEntryClickedEventHandler(DataGridViewCellEventArgs e, LibraryBookEntry liveGridEntry); + internal delegate void LibraryBookEntryRectangleClickedEventHandler(DataGridViewCellEventArgs e, LibraryBookEntry liveGridEntry, Rectangle cellRectangle); + internal event LibraryBookEntryClickedEventHandler LiberateClicked; + internal event LibraryBookEntryClickedEventHandler CoverClicked; + internal event LibraryBookEntryClickedEventHandler DetailsClicked; + internal event LibraryBookEntryRectangleClickedEventHandler DescriptionClicked; + public new event EventHandler Scroll; + + private FilterableSortableBindingList bindingList; + /// Number of visible rows has changed public event EventHandler VisibleCountChanged; @@ -53,8 +60,14 @@ namespace LibationWinForms EnableDoubleBuffering(); _dataGridView.CellContentClick += DataGridView_CellContentClick; + _dataGridView.Scroll += (_, s) => Scroll?.Invoke(this, s); - this.Load += ProductsGrid_Load; + Load += ProductsGrid_Load; + } + + private void ProductsGrid_Scroll(object sender, ScrollEventArgs e) + { + throw new NotImplementedException(); } private void EnableDoubleBuffering() @@ -66,117 +79,70 @@ namespace LibationWinForms #region Button controls - private async void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e) + private void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e) { // handle grid button click: https://stackoverflow.com/a/13687844 if (e.RowIndex < 0) return; - if (e.ColumnIndex == liberateGVColumn.Index) - Liberate_Click(getGridEntry(e.RowIndex)); - else if (e.ColumnIndex == tagAndDetailsGVColumn.Index) - Details_Click(getGridEntry(e.RowIndex)); - else if (e.ColumnIndex == descriptionGVColumn.Index) - Description_Click(getGridEntry(e.RowIndex), _dataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false)); - else if (e.ColumnIndex == coverGVColumn.Index) - await Cover_Click(getGridEntry(e.RowIndex)); - } - - private ImageDisplay imageDisplay; - private async Task Cover_Click(GridEntry liveGridEntry) - { - var picDefinition = new PictureDefinition(liveGridEntry.LibraryBook.Book.PictureLarge ?? liveGridEntry.LibraryBook.Book.PictureId, PictureSize.Native); - var picDlTask = Task.Run(() => PictureStorage.GetPictureSynchronously(picDefinition)); - - (_, byte[] initialImageBts) = PictureStorage.GetPicture(new PictureDefinition(liveGridEntry.LibraryBook.Book.PictureId, PictureSize._80x80)); - var windowTitle = $"{liveGridEntry.Title} - Cover"; - - if (imageDisplay is null || imageDisplay.IsDisposed || !imageDisplay.Visible) + var entry = getGridEntry(e.RowIndex); + if (entry is LibraryBookEntry lbEntry) { - imageDisplay = new ImageDisplay(); - imageDisplay.RestoreSizeAndLocation(Configuration.Instance); - imageDisplay.FormClosed += (_, _) => imageDisplay.SaveSizeAndLocation(Configuration.Instance); - imageDisplay.Show(this); + if (e.ColumnIndex == liberateGVColumn.Index) + LiberateClicked?.Invoke(e, lbEntry); + else if (e.ColumnIndex == tagAndDetailsGVColumn.Index && entry is LibraryBookEntry) + DetailsClicked?.Invoke(e, lbEntry); + else if (e.ColumnIndex == descriptionGVColumn.Index) + DescriptionClicked?.Invoke(e, lbEntry, _dataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false)); + else if (e.ColumnIndex == coverGVColumn.Index) + CoverClicked?.Invoke(e, lbEntry); } - - imageDisplay.BookSaveDirectory = AudibleFileStorage.Audio.GetDestinationDirectory(liveGridEntry.LibraryBook); - imageDisplay.PictureFileName = System.IO.Path.GetFileName(AudibleFileStorage.Audio.GetBooksDirectoryFilename(liveGridEntry.LibraryBook, ".jpg")); - imageDisplay.Text = windowTitle; - imageDisplay.CoverPicture = initialImageBts; - imageDisplay.CoverPicture = await picDlTask; - } - - private void Description_Click(GridEntry liveGridEntry, Rectangle cellDisplay) - { - var displayWindow = new DescriptionDisplay + else if (entry is SeriesEntry sEntry && e.ColumnIndex == liberateGVColumn.Index) { - SpawnLocation = PointToScreen(cellDisplay.Location + new Size(cellDisplay.Width, 0)), - DescriptionText = liveGridEntry.LongDescription, - BorderThickness = 2, - }; + if (sEntry.Liberate.Expanded) + bindingList.CollapseItem(sEntry); + else + bindingList.ExpandItem(sEntry); - void CloseWindow(object o, EventArgs e) - { - displayWindow.Close(); + sEntry.NotifyPropertyChanged(nameof(sEntry.Liberate)); } - - _dataGridView.Scroll += CloseWindow; - displayWindow.FormClosed += (_, _) => _dataGridView.Scroll -= CloseWindow; - displayWindow.Show(this); } - private void Liberate_Click(GridEntry liveGridEntry) - { - LiberateClicked?.Invoke(this, liveGridEntry.LibraryBook); - } - - private static void Details_Click(GridEntry liveGridEntry) - { - var bookDetailsForm = new BookDetailsDialog(liveGridEntry.LibraryBook); - if (bookDetailsForm.ShowDialog() == DialogResult.OK) - liveGridEntry.Commit(bookDetailsForm.NewTags, bookDetailsForm.BookLiberatedStatus, bookDetailsForm.PdfLiberatedStatus); - } + private GridEntry getGridEntry(int rowIndex) => _dataGridView.GetBoundItem(rowIndex); #endregion #region UI display functions - private FilterableSortableBindingList bindingList; - - private bool hasBeenDisplayed; - public event EventHandler InitialLoaded; - public void Display() + internal void bindToGrid(List dbBooks) { - // don't return early if lib size == 0. this will not update correctly if all books are removed - var lib = DbContexts.GetLibrary_Flat_NoTracking(); + var geList = dbBooks.Where(b => b.Book.ContentType is not ContentType.Episode).Select(b => new LibraryBookEntry(b)).Cast().ToList(); - if (!hasBeenDisplayed) + 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) { - // bind - bindToGrid(lib); - hasBeenDisplayed = true; - InitialLoaded?.Invoke(this, new()); - VisibleCountChanged?.Invoke(this, bindingList.Count); + 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 }).Cast().ToList(); + + seriesEntry.setSeriesBook(s); + geList.Add(seriesEntry); } - else - updateGrid(lib); - } - - private void bindToGrid(List dbBooks) - { - bindingList = new FilterableSortableBindingList(dbBooks.OrderByDescending(lb => lb.DateAdded).Select(lb => new GridEntry(lb))); + bindingList = new FilterableSortableBindingList(geList.OrderByDescending(ge => ge.DateAdded)); gridEntryBindingSource.DataSource = bindingList; } - private void updateGrid(List dbBooks) + internal void updateGrid(List dbBooks) { int visibleCount = bindingList.Count; string existingFilter = gridEntryBindingSource.Filter; //Add absent books to grid, or update current books - var allItmes = bindingList.AllItems(); + var allItmes = bindingList.AllItems().Where(i => i is LibraryBookEntry).Cast(); for (var i = dbBooks.Count - 1; i >= 0; i--) { var libraryBook = dbBooks[i]; @@ -184,10 +150,37 @@ namespace LibationWinForms // add new to top if (existingItem is null) - bindingList.Insert(0, new GridEntry(libraryBook)); + { + var lb = new LibraryBookEntry(libraryBook); + + if (libraryBook.Book.ContentType is ContentType.Episode) + { + //Find the series that libraryBook, if it exists + var series = bindingList.AllItems().Where(i => i is SeriesEntry).Cast().FirstOrDefault(i => libraryBook.Book.SeriesLink.Any(s => s.Series.Name == i.Series)); + + 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; + newSeries.Liberate.Expanded = true; + bindingList.Insert(0, newSeries); + } + else + { + lb.Parent = series; + series.Children.Add(lb); + } + } + //Add the new product + bindingList.Insert(0, lb); + } // update existing else + { existingItem.UpdateLibraryBook(libraryBook); + } } if (bindingList.Count != visibleCount) @@ -199,13 +192,22 @@ namespace LibationWinForms // 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 = + var removedBooks = bindingList .AllItems() - .ExceptBy(dbBooks.Select(lb => lb.Book.AudibleProductId), ge => ge.AudibleProductId) - .ToList(); + .Where(i => i is LibraryBookEntry) + .Cast() + .ExceptBy(dbBooks.Select(lb => lb.Book.AudibleProductId), ge => ge.AudibleProductId); - foreach (var removed in removedBooks) + //Remove series that have no children + var removedSeries = + bindingList + .AllItems() + .Where(i => i is SeriesEntry) + .Cast() + .Where(i => removedBooks.Count(r => r.Series == i.Series) == i.Children.Count); + + foreach (var removed in removedBooks.Cast().Concat(removedSeries)) //no need to re-filter for removed books bindingList.Remove(removed); @@ -232,12 +234,11 @@ namespace LibationWinForms #endregion - internal List GetVisible() + internal IEnumerable GetVisible() => bindingList - .Select(row => row.LibraryBook) - .ToList(); - - private GridEntry getGridEntry(int rowIndex) => _dataGridView.GetBoundItem(rowIndex); + .Where(row => row is LibraryBookEntry) + .Cast() + .Select(row => row.LibraryBook); #region Column Customizations @@ -293,8 +294,6 @@ namespace LibationWinForms column.DisplayIndex = displayIndices.GetValueOrDefault(itemName, column.Index); } - - base.OnVisibleChanged(e); } private void gridEntryDataGridView_ColumnDisplayIndexChanged(object sender, DataGridViewColumnEventArgs e) diff --git a/Source/LibationWinForms/grid/ProductsGrid.resx b/Source/LibationWinForms/grid/ProductsGrid.resx index 8a560af5..be5db7af 100644 --- a/Source/LibationWinForms/grid/ProductsGrid.resx +++ b/Source/LibationWinForms/grid/ProductsGrid.resx @@ -57,12 +57,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 17, 17 - - - 197, 17 - 81 diff --git a/Source/LibationWinForms/grid/SeriesEntry.cs b/Source/LibationWinForms/grid/SeriesEntry.cs new file mode 100644 index 00000000..6a1e51b8 --- /dev/null +++ b/Source/LibationWinForms/grid/SeriesEntry.cs @@ -0,0 +1,90 @@ +using DataLayer; +using Dinah.Core; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LibationWinForms +{ + internal class SeriesEntry : GridEntry + { + public override DateTime DateAdded => Children.Max(c => c.DateAdded); + public override string ProductRating { get; protected set; } + public override string PurchaseDate { get; protected set; } + public override string MyRating { get; protected set; } + public override string Series { get; protected set; } + public override string Title { get; protected set; } + public override string Length { get; protected set; } + public override string Authors { get; protected set; } + public override string Narrators { get; protected set; } + public override string Category { get; protected set; } + public override string Misc { get; protected set; } + public override string Description { get; protected set; } + public override string DisplayTags => string.Empty; + + public override LiberateStatus Liberate => _liberate; + + protected override Book Book => SeriesBook.Book; + + private SeriesBook SeriesBook { get; set; } + + private LiberateStatus _liberate = new LiberateStatus { IsSeries = true }; + public void setSeriesBook(SeriesBook seriesBook) + { + SeriesBook = seriesBook; + _memberValues = CreateMemberValueDictionary(); + LoadCover(); + + // Immutable properties + { + var childLB = Children.Cast(); + int bookLenMins = childLB.Sum(c => c.LibraryBook.Book.LengthInMinutes); + + var myAverageRating = new Rating(childLB.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.OverallRating), childLB.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.PerformanceRating), childLB.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.StoryRating)); + var productAverageRating = new Rating(childLB.Average(c => c.LibraryBook.Book.Rating.OverallRating), childLB.Average(c => c.LibraryBook.Book.Rating.PerformanceRating), childLB.Average(c => c.LibraryBook.Book.Rating.StoryRating)); + + + Title = SeriesBook.Series.Name; + Series = SeriesBook.Series.Name; + Length = bookLenMins == 0 ? "" : $"{bookLenMins / 60} hr {bookLenMins % 60} min"; + MyRating = myAverageRating.ToStarString()?.DefaultIfNullOrWhiteSpace(""); + PurchaseDate = childLB.Min(c => c.LibraryBook.DateAdded).ToString("d"); + ProductRating = productAverageRating.ToStarString()?.DefaultIfNullOrWhiteSpace(""); + Authors = Book.AuthorNames(); + Narrators = Book.NarratorNames(); + Category = string.Join(" > ", Book.CategoriesNames()); + } + } + + // 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() + { + { nameof(Title), () => Book.SeriesSortable() }, + { nameof(Series), () => Book.SeriesSortable() }, + { nameof(Length), () => Children.Cast().Sum(c=>c.LibraryBook.Book.LengthInMinutes) }, + { nameof(MyRating), () => Children.Cast().Average(c=>c.LibraryBook.Book.UserDefinedItem.Rating.FirstScore()) }, + { nameof(PurchaseDate), () => Children.Cast().Min(c=>c.LibraryBook.DateAdded) }, + { nameof(ProductRating), () => Children.Cast().Average(c => c.LibraryBook.Book.Rating.FirstScore()) }, + { nameof(Authors), () => string.Empty }, + { nameof(Narrators), () => string.Empty }, + { nameof(Description), () => string.Empty }, + { nameof(Category), () => string.Empty }, + { nameof(Misc), () => string.Empty }, + { nameof(DisplayTags), () => string.Empty }, + { nameof(Liberate), () => Liberate.BookStatus }, + { nameof(DateAdded), () => DateAdded }, + }; + } +} diff --git a/Source/LibationWinForms/grid/SortableBindingList1.cs b/Source/LibationWinForms/grid/SortableBindingList1.cs new file mode 100644 index 00000000..7d12819f --- /dev/null +++ b/Source/LibationWinForms/grid/SortableBindingList1.cs @@ -0,0 +1,111 @@ +using Dinah.Core.DataBinding; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +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 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; + + //Array.Sort() and List.Sort() are unstable sorts. OrderBy is stable. + var sortedItems = itemsList.OrderBy((ge) => ge, Comparer).ToList(); + + var children = sortedItems.Where(i => i.Parent is not null).ToList(); + var parents = sortedItems.Where(i => i.Children is not null).ToList(); + + //Top Level items + var topLevelItems = sortedItems.Except(children); + + itemsList.Clear(); + itemsList.AddRange(topLevelItems); + + foreach (var p in parents) + { + var pIndex = itemsList.IndexOf(p); + foreach (var c in children.Where(c=> c.Parent == p)) + itemsList.Insert(++pIndex, c); + } + } + + protected override void OnListChanged(ListChangedEventArgs e) + { + if (isSorted && + ((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)); + } + + protected override int FindCore(PropertyDescriptor property, object key) + { + int count = Count; + + System.Collections.IComparer valueComparer = null; + + for (int i = 0; i < count; ++i) + { + var element = this[i]; + var elemValue = element.GetMemberValue(property.Name); + valueComparer ??= element.GetMemberComparer(elemValue.GetType()); + + if (valueComparer.Compare(elemValue, key) == 0) + { + return i; + } + } + + return -1; + } + } +} From 593fe57ea1c35a3b20107d6111b8fbe444aed5fd Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Mon, 23 May 2022 15:29:26 -0600 Subject: [PATCH 03/15] Refactor ProductsGrid --- Source/LibationWinForms/Form1.Designer.cs | 939 +++++++++--------- Source/LibationWinForms/Form1.Filter.cs | 2 +- Source/LibationWinForms/Form1.ProcessQueue.cs | 3 +- Source/LibationWinForms/Form1.QuickFilters.cs | 12 +- Source/LibationWinForms/Form1.VisibleBooks.cs | 37 +- Source/LibationWinForms/Form1.cs | 18 +- .../LibationWinForms/LibationWinForms.csproj | 6 - .../EditTagsDataGridViewImageButtonColumn.cs | 9 +- .../grid/FilterableSortableBindingList.cs | 99 +- Source/LibationWinForms/grid/GridEntry.cs | 42 +- .../LibationWinForms/grid/LibraryBookEntry.cs | 4 +- .../grid/MasterDataGridView.cs | 26 - .../grid/ProductsDisplay.Designer.cs | 19 + .../LibationWinForms/grid/ProductsDisplay.cs | 69 +- .../grid/ProductsGrid.Designer.cs | 48 +- Source/LibationWinForms/grid/ProductsGrid.cs | 205 ++-- .../LibationWinForms/grid/ProductsGrid.resx | 4 +- Source/LibationWinForms/grid/SeriesEntry.cs | 22 +- .../grid/SortableBindingList1.cs | 41 +- 19 files changed, 768 insertions(+), 837 deletions(-) delete mode 100644 Source/LibationWinForms/grid/MasterDataGridView.cs diff --git a/Source/LibationWinForms/Form1.Designer.cs b/Source/LibationWinForms/Form1.Designer.cs index 1f554c16..98a7b9f7 100644 --- a/Source/LibationWinForms/Form1.Designer.cs +++ b/Source/LibationWinForms/Form1.Designer.cs @@ -28,111 +28,100 @@ /// private void InitializeComponent() { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1)); - this.gridPanel = new System.Windows.Forms.Panel(); - this.filterHelpBtn = new System.Windows.Forms.Button(); - this.filterBtn = new System.Windows.Forms.Button(); - this.filterSearchTb = new System.Windows.Forms.TextBox(); - this.menuStrip1 = new System.Windows.Forms.MenuStrip(); - this.importToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.autoScanLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.noAccountsYetAddAccountToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.scanLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.scanLibraryOfAllAccountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.scanLibraryOfSomeAccountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.removeLibraryBooksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.removeAllAccountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.removeSomeAccountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.liberateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.beginBookBackupsToolStripMenuItem = new LibationWinForms.FormattableToolStripMenuItem(); - this.beginPdfBackupsToolStripMenuItem = new LibationWinForms.FormattableToolStripMenuItem(); - this.convertAllM4bToMp3ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.liberateVisible2ToolStripMenuItem = new LibationWinForms.FormattableToolStripMenuItem(); - this.exportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.exportLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.quickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.firstFilterIsDefaultToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.editQuickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); - this.scanningToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.visibleBooksToolStripMenuItem = new LibationWinForms.FormattableToolStripMenuItem(); - this.liberateVisibleToolStripMenuItem = new LibationWinForms.FormattableToolStripMenuItem(); - this.replaceTagsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.setDownloadedToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.removeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.accountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.basicSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); - this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.statusStrip1 = new System.Windows.Forms.StatusStrip(); - this.visibleCountLbl = new LibationWinForms.FormattableToolStripStatusLabel(); - this.springLbl = new System.Windows.Forms.ToolStripStatusLabel(); - this.backupsCountsLbl = new System.Windows.Forms.ToolStripStatusLabel(); - this.pdfsCountsLbl = new LibationWinForms.FormattableToolStripStatusLabel(); - this.addQuickFilterBtn = new System.Windows.Forms.Button(); - this.splitContainer1 = new System.Windows.Forms.SplitContainer(); - this.panel1 = new System.Windows.Forms.Panel(); - this.toggleQueueHideBtn = new System.Windows.Forms.Button(); - this.processBookQueue1 = new LibationWinForms.ProcessQueue.ProcessQueueControl(); - this.menuStrip1.SuspendLayout(); - this.statusStrip1.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); - this.splitContainer1.Panel1.SuspendLayout(); - this.splitContainer1.Panel2.SuspendLayout(); - this.splitContainer1.SuspendLayout(); - this.panel1.SuspendLayout(); - this.SuspendLayout(); - // - // gridPanel - // - this.gridPanel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1)); + this.filterHelpBtn = new System.Windows.Forms.Button(); + this.filterBtn = new System.Windows.Forms.Button(); + this.filterSearchTb = new System.Windows.Forms.TextBox(); + this.menuStrip1 = new System.Windows.Forms.MenuStrip(); + this.importToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.autoScanLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.noAccountsYetAddAccountToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.scanLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.scanLibraryOfAllAccountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.scanLibraryOfSomeAccountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.removeLibraryBooksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.removeAllAccountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.removeSomeAccountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.liberateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.beginBookBackupsToolStripMenuItem = new LibationWinForms.FormattableToolStripMenuItem(); + this.beginPdfBackupsToolStripMenuItem = new LibationWinForms.FormattableToolStripMenuItem(); + this.convertAllM4bToMp3ToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.liberateVisible2ToolStripMenuItem = new LibationWinForms.FormattableToolStripMenuItem(); + this.exportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.exportLibraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.quickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.firstFilterIsDefaultToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.editQuickFiltersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.scanningToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.visibleBooksToolStripMenuItem = new LibationWinForms.FormattableToolStripMenuItem(); + this.liberateVisibleToolStripMenuItem = new LibationWinForms.FormattableToolStripMenuItem(); + this.replaceTagsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.setDownloadedToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.removeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.accountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.basicSettingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.statusStrip1 = new System.Windows.Forms.StatusStrip(); + this.visibleCountLbl = new LibationWinForms.FormattableToolStripStatusLabel(); + this.springLbl = new System.Windows.Forms.ToolStripStatusLabel(); + this.backupsCountsLbl = new System.Windows.Forms.ToolStripStatusLabel(); + this.pdfsCountsLbl = new LibationWinForms.FormattableToolStripStatusLabel(); + this.addQuickFilterBtn = new System.Windows.Forms.Button(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.panel1 = new System.Windows.Forms.Panel(); + this.productsDisplay = new LibationWinForms.ProductsDisplay(); + this.toggleQueueHideBtn = new System.Windows.Forms.Button(); + this.processBookQueue1 = new LibationWinForms.ProcessQueue.ProcessQueueControl(); + this.menuStrip1.SuspendLayout(); + this.statusStrip1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // filterHelpBtn + // + this.filterHelpBtn.Location = new System.Drawing.Point(15, 3); + this.filterHelpBtn.Margin = new System.Windows.Forms.Padding(15, 3, 4, 3); + this.filterHelpBtn.Name = "filterHelpBtn"; + this.filterHelpBtn.Size = new System.Drawing.Size(26, 27); + this.filterHelpBtn.TabIndex = 3; + this.filterHelpBtn.Text = "?"; + this.filterHelpBtn.UseVisualStyleBackColor = true; + this.filterHelpBtn.Click += new System.EventHandler(this.filterHelpBtn_Click); + // + // 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.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + this.filterBtn.Name = "filterBtn"; + this.filterBtn.Size = new System.Drawing.Size(88, 27); + this.filterBtn.TabIndex = 2; + this.filterBtn.Text = "Filter"; + this.filterBtn.UseVisualStyleBackColor = true; + this.filterBtn.Click += new System.EventHandler(this.filterBtn_Click); + // + // filterSearchTb + // + this.filterSearchTb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.gridPanel.Location = new System.Drawing.Point(15, 33); - this.gridPanel.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.gridPanel.Name = "gridPanel"; - this.gridPanel.Size = new System.Drawing.Size(864, 558); - this.gridPanel.TabIndex = 5; - // - // filterHelpBtn - // - this.filterHelpBtn.Location = new System.Drawing.Point(15, 3); - this.filterHelpBtn.Margin = new System.Windows.Forms.Padding(15, 3, 4, 3); - this.filterHelpBtn.Name = "filterHelpBtn"; - this.filterHelpBtn.Size = new System.Drawing.Size(26, 27); - this.filterHelpBtn.TabIndex = 3; - this.filterHelpBtn.Text = "?"; - this.filterHelpBtn.UseVisualStyleBackColor = true; - this.filterHelpBtn.Click += new System.EventHandler(this.filterHelpBtn_Click); - // - // 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.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.filterBtn.Name = "filterBtn"; - this.filterBtn.Size = new System.Drawing.Size(88, 27); - this.filterBtn.TabIndex = 2; - this.filterBtn.Text = "Filter"; - this.filterBtn.UseVisualStyleBackColor = true; - this.filterBtn.Click += new System.EventHandler(this.filterBtn_Click); - // - // filterSearchTb - // - this.filterSearchTb.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - 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.TabIndex = 1; - this.filterSearchTb.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.filterSearchTb_KeyPress); - // - // menuStrip1 - // - this.menuStrip1.ImageScalingSize = new System.Drawing.Size(40, 40); - this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + 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.TabIndex = 1; + this.filterSearchTb.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.filterSearchTb_KeyPress); + // + // menuStrip1 + // + this.menuStrip1.ImageScalingSize = new System.Drawing.Size(40, 40); + this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.importToolStripMenuItem, this.liberateToolStripMenuItem, this.exportToolStripMenuItem, @@ -140,402 +129,415 @@ this.scanningToolStripMenuItem, this.visibleBooksToolStripMenuItem, this.settingsToolStripMenuItem}); - 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.TabIndex = 0; - this.menuStrip1.Text = "menuStrip1"; - // - // importToolStripMenuItem - // - this.importToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + 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.TabIndex = 0; + this.menuStrip1.Text = "menuStrip1"; + // + // importToolStripMenuItem + // + this.importToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.autoScanLibraryToolStripMenuItem, this.noAccountsYetAddAccountToolStripMenuItem, this.scanLibraryToolStripMenuItem, this.scanLibraryOfAllAccountsToolStripMenuItem, this.scanLibraryOfSomeAccountsToolStripMenuItem, this.removeLibraryBooksToolStripMenuItem}); - this.importToolStripMenuItem.Name = "importToolStripMenuItem"; - this.importToolStripMenuItem.Size = new System.Drawing.Size(55, 20); - this.importToolStripMenuItem.Text = "&Import"; - // - // autoScanLibraryToolStripMenuItem - // - this.autoScanLibraryToolStripMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; - this.autoScanLibraryToolStripMenuItem.Name = "autoScanLibraryToolStripMenuItem"; - this.autoScanLibraryToolStripMenuItem.Size = new System.Drawing.Size(247, 22); - this.autoScanLibraryToolStripMenuItem.Text = "A&uto Scan Library"; - this.autoScanLibraryToolStripMenuItem.Click += new System.EventHandler(this.autoScanLibraryToolStripMenuItem_Click); - // - // noAccountsYetAddAccountToolStripMenuItem - // - this.noAccountsYetAddAccountToolStripMenuItem.Name = "noAccountsYetAddAccountToolStripMenuItem"; - this.noAccountsYetAddAccountToolStripMenuItem.Size = new System.Drawing.Size(247, 22); - this.noAccountsYetAddAccountToolStripMenuItem.Text = "No accounts yet. A&dd Account..."; - this.noAccountsYetAddAccountToolStripMenuItem.Click += new System.EventHandler(this.noAccountsYetAddAccountToolStripMenuItem_Click); - // - // scanLibraryToolStripMenuItem - // - this.scanLibraryToolStripMenuItem.Name = "scanLibraryToolStripMenuItem"; - this.scanLibraryToolStripMenuItem.Size = new System.Drawing.Size(247, 22); - this.scanLibraryToolStripMenuItem.Text = "Scan &Library"; - this.scanLibraryToolStripMenuItem.Click += new System.EventHandler(this.scanLibraryToolStripMenuItem_Click); - // - // scanLibraryOfAllAccountsToolStripMenuItem - // - this.scanLibraryOfAllAccountsToolStripMenuItem.Name = "scanLibraryOfAllAccountsToolStripMenuItem"; - this.scanLibraryOfAllAccountsToolStripMenuItem.Size = new System.Drawing.Size(247, 22); - this.scanLibraryOfAllAccountsToolStripMenuItem.Text = "Scan Library of &All Accounts"; - this.scanLibraryOfAllAccountsToolStripMenuItem.Click += new System.EventHandler(this.scanLibraryOfAllAccountsToolStripMenuItem_Click); - // - // scanLibraryOfSomeAccountsToolStripMenuItem - // - this.scanLibraryOfSomeAccountsToolStripMenuItem.Name = "scanLibraryOfSomeAccountsToolStripMenuItem"; - this.scanLibraryOfSomeAccountsToolStripMenuItem.Size = new System.Drawing.Size(247, 22); - this.scanLibraryOfSomeAccountsToolStripMenuItem.Text = "Scan Library of &Some Accounts..."; - this.scanLibraryOfSomeAccountsToolStripMenuItem.Click += new System.EventHandler(this.scanLibraryOfSomeAccountsToolStripMenuItem_Click); - // - // removeLibraryBooksToolStripMenuItem - // - this.removeLibraryBooksToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.importToolStripMenuItem.Name = "importToolStripMenuItem"; + this.importToolStripMenuItem.Size = new System.Drawing.Size(55, 20); + this.importToolStripMenuItem.Text = "&Import"; + // + // autoScanLibraryToolStripMenuItem + // + this.autoScanLibraryToolStripMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.autoScanLibraryToolStripMenuItem.Name = "autoScanLibraryToolStripMenuItem"; + this.autoScanLibraryToolStripMenuItem.Size = new System.Drawing.Size(247, 22); + this.autoScanLibraryToolStripMenuItem.Text = "A&uto Scan Library"; + this.autoScanLibraryToolStripMenuItem.Click += new System.EventHandler(this.autoScanLibraryToolStripMenuItem_Click); + // + // noAccountsYetAddAccountToolStripMenuItem + // + this.noAccountsYetAddAccountToolStripMenuItem.Name = "noAccountsYetAddAccountToolStripMenuItem"; + this.noAccountsYetAddAccountToolStripMenuItem.Size = new System.Drawing.Size(247, 22); + this.noAccountsYetAddAccountToolStripMenuItem.Text = "No accounts yet. A&dd Account..."; + this.noAccountsYetAddAccountToolStripMenuItem.Click += new System.EventHandler(this.noAccountsYetAddAccountToolStripMenuItem_Click); + // + // scanLibraryToolStripMenuItem + // + this.scanLibraryToolStripMenuItem.Name = "scanLibraryToolStripMenuItem"; + this.scanLibraryToolStripMenuItem.Size = new System.Drawing.Size(247, 22); + this.scanLibraryToolStripMenuItem.Text = "Scan &Library"; + this.scanLibraryToolStripMenuItem.Click += new System.EventHandler(this.scanLibraryToolStripMenuItem_Click); + // + // scanLibraryOfAllAccountsToolStripMenuItem + // + this.scanLibraryOfAllAccountsToolStripMenuItem.Name = "scanLibraryOfAllAccountsToolStripMenuItem"; + this.scanLibraryOfAllAccountsToolStripMenuItem.Size = new System.Drawing.Size(247, 22); + this.scanLibraryOfAllAccountsToolStripMenuItem.Text = "Scan Library of &All Accounts"; + this.scanLibraryOfAllAccountsToolStripMenuItem.Click += new System.EventHandler(this.scanLibraryOfAllAccountsToolStripMenuItem_Click); + // + // scanLibraryOfSomeAccountsToolStripMenuItem + // + this.scanLibraryOfSomeAccountsToolStripMenuItem.Name = "scanLibraryOfSomeAccountsToolStripMenuItem"; + this.scanLibraryOfSomeAccountsToolStripMenuItem.Size = new System.Drawing.Size(247, 22); + this.scanLibraryOfSomeAccountsToolStripMenuItem.Text = "Scan Library of &Some Accounts..."; + this.scanLibraryOfSomeAccountsToolStripMenuItem.Click += new System.EventHandler(this.scanLibraryOfSomeAccountsToolStripMenuItem_Click); + // + // removeLibraryBooksToolStripMenuItem + // + this.removeLibraryBooksToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.removeAllAccountsToolStripMenuItem, this.removeSomeAccountsToolStripMenuItem}); - this.removeLibraryBooksToolStripMenuItem.Name = "removeLibraryBooksToolStripMenuItem"; - this.removeLibraryBooksToolStripMenuItem.Size = new System.Drawing.Size(247, 22); - this.removeLibraryBooksToolStripMenuItem.Text = "Remove Library Books"; - this.removeLibraryBooksToolStripMenuItem.Click += new System.EventHandler(this.removeLibraryBooksToolStripMenuItem_Click); - // - // removeAllAccountsToolStripMenuItem - // - this.removeAllAccountsToolStripMenuItem.Name = "removeAllAccountsToolStripMenuItem"; - this.removeAllAccountsToolStripMenuItem.Size = new System.Drawing.Size(157, 22); - this.removeAllAccountsToolStripMenuItem.Text = "All Accounts"; - this.removeAllAccountsToolStripMenuItem.Click += new System.EventHandler(this.removeAllAccountsToolStripMenuItem_Click); - // - // removeSomeAccountsToolStripMenuItem - // - this.removeSomeAccountsToolStripMenuItem.Name = "removeSomeAccountsToolStripMenuItem"; - this.removeSomeAccountsToolStripMenuItem.Size = new System.Drawing.Size(157, 22); - this.removeSomeAccountsToolStripMenuItem.Text = "Some Accounts"; - this.removeSomeAccountsToolStripMenuItem.Click += new System.EventHandler(this.removeSomeAccountsToolStripMenuItem_Click); - // - // liberateToolStripMenuItem - // - this.liberateToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.removeLibraryBooksToolStripMenuItem.Name = "removeLibraryBooksToolStripMenuItem"; + this.removeLibraryBooksToolStripMenuItem.Size = new System.Drawing.Size(247, 22); + this.removeLibraryBooksToolStripMenuItem.Text = "Remove Library Books"; + this.removeLibraryBooksToolStripMenuItem.Click += new System.EventHandler(this.removeLibraryBooksToolStripMenuItem_Click); + // + // removeAllAccountsToolStripMenuItem + // + this.removeAllAccountsToolStripMenuItem.Name = "removeAllAccountsToolStripMenuItem"; + this.removeAllAccountsToolStripMenuItem.Size = new System.Drawing.Size(157, 22); + this.removeAllAccountsToolStripMenuItem.Text = "All Accounts"; + this.removeAllAccountsToolStripMenuItem.Click += new System.EventHandler(this.removeAllAccountsToolStripMenuItem_Click); + // + // removeSomeAccountsToolStripMenuItem + // + this.removeSomeAccountsToolStripMenuItem.Name = "removeSomeAccountsToolStripMenuItem"; + this.removeSomeAccountsToolStripMenuItem.Size = new System.Drawing.Size(157, 22); + this.removeSomeAccountsToolStripMenuItem.Text = "Some Accounts"; + this.removeSomeAccountsToolStripMenuItem.Click += new System.EventHandler(this.removeSomeAccountsToolStripMenuItem_Click); + // + // liberateToolStripMenuItem + // + this.liberateToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.beginBookBackupsToolStripMenuItem, this.beginPdfBackupsToolStripMenuItem, this.convertAllM4bToMp3ToolStripMenuItem, this.liberateVisible2ToolStripMenuItem}); - this.liberateToolStripMenuItem.Name = "liberateToolStripMenuItem"; - this.liberateToolStripMenuItem.Size = new System.Drawing.Size(61, 20); - this.liberateToolStripMenuItem.Text = "&Liberate"; - // - // beginBookBackupsToolStripMenuItem - // - this.beginBookBackupsToolStripMenuItem.FormatText = "Begin &Book and PDF Backups: {0}"; - this.beginBookBackupsToolStripMenuItem.Name = "beginBookBackupsToolStripMenuItem"; - this.beginBookBackupsToolStripMenuItem.Size = new System.Drawing.Size(293, 22); - this.beginBookBackupsToolStripMenuItem.Text = "Begin &Book and PDF Backups: {0}"; - this.beginBookBackupsToolStripMenuItem.Click += new System.EventHandler(this.beginBookBackupsToolStripMenuItem_Click); - // - // beginPdfBackupsToolStripMenuItem - // - this.beginPdfBackupsToolStripMenuItem.FormatText = "Begin &PDF Only Backups: {0}"; - this.beginPdfBackupsToolStripMenuItem.Name = "beginPdfBackupsToolStripMenuItem"; - this.beginPdfBackupsToolStripMenuItem.Size = new System.Drawing.Size(293, 22); - this.beginPdfBackupsToolStripMenuItem.Text = "Begin &PDF Only Backups: {0}"; - this.beginPdfBackupsToolStripMenuItem.Click += new System.EventHandler(this.beginPdfBackupsToolStripMenuItem_Click); - // - // convertAllM4bToMp3ToolStripMenuItem - // - this.convertAllM4bToMp3ToolStripMenuItem.Name = "convertAllM4bToMp3ToolStripMenuItem"; - this.convertAllM4bToMp3ToolStripMenuItem.Size = new System.Drawing.Size(293, 22); - this.convertAllM4bToMp3ToolStripMenuItem.Text = "Convert all &M4b to Mp3 [Long-running]..."; - this.convertAllM4bToMp3ToolStripMenuItem.Click += new System.EventHandler(this.convertAllM4bToMp3ToolStripMenuItem_Click); - // - // liberateVisible2ToolStripMenuItem - // - this.liberateVisible2ToolStripMenuItem.FormatText = "Liberate &Visible Books: {0}"; - this.liberateVisible2ToolStripMenuItem.Name = "liberateVisible2ToolStripMenuItem"; - this.liberateVisible2ToolStripMenuItem.Size = new System.Drawing.Size(293, 22); - this.liberateVisible2ToolStripMenuItem.Text = "Liberate &Visible Books: {0}"; - this.liberateVisible2ToolStripMenuItem.Click += new System.EventHandler(this.liberateVisible); - // - // exportToolStripMenuItem - // - this.exportToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.liberateToolStripMenuItem.Name = "liberateToolStripMenuItem"; + this.liberateToolStripMenuItem.Size = new System.Drawing.Size(61, 20); + this.liberateToolStripMenuItem.Text = "&Liberate"; + // + // beginBookBackupsToolStripMenuItem + // + this.beginBookBackupsToolStripMenuItem.FormatText = "Begin &Book and PDF Backups: {0}"; + this.beginBookBackupsToolStripMenuItem.Name = "beginBookBackupsToolStripMenuItem"; + this.beginBookBackupsToolStripMenuItem.Size = new System.Drawing.Size(293, 22); + this.beginBookBackupsToolStripMenuItem.Text = "Begin &Book and PDF Backups: {0}"; + this.beginBookBackupsToolStripMenuItem.Click += new System.EventHandler(this.beginBookBackupsToolStripMenuItem_Click); + // + // beginPdfBackupsToolStripMenuItem + // + this.beginPdfBackupsToolStripMenuItem.FormatText = "Begin &PDF Only Backups: {0}"; + this.beginPdfBackupsToolStripMenuItem.Name = "beginPdfBackupsToolStripMenuItem"; + this.beginPdfBackupsToolStripMenuItem.Size = new System.Drawing.Size(293, 22); + this.beginPdfBackupsToolStripMenuItem.Text = "Begin &PDF Only Backups: {0}"; + this.beginPdfBackupsToolStripMenuItem.Click += new System.EventHandler(this.beginPdfBackupsToolStripMenuItem_Click); + // + // convertAllM4bToMp3ToolStripMenuItem + // + this.convertAllM4bToMp3ToolStripMenuItem.Name = "convertAllM4bToMp3ToolStripMenuItem"; + this.convertAllM4bToMp3ToolStripMenuItem.Size = new System.Drawing.Size(293, 22); + this.convertAllM4bToMp3ToolStripMenuItem.Text = "Convert all &M4b to Mp3 [Long-running]..."; + this.convertAllM4bToMp3ToolStripMenuItem.Click += new System.EventHandler(this.convertAllM4bToMp3ToolStripMenuItem_Click); + // + // liberateVisible2ToolStripMenuItem + // + this.liberateVisible2ToolStripMenuItem.FormatText = "Liberate &Visible Books: {0}"; + this.liberateVisible2ToolStripMenuItem.Name = "liberateVisible2ToolStripMenuItem"; + this.liberateVisible2ToolStripMenuItem.Size = new System.Drawing.Size(293, 22); + this.liberateVisible2ToolStripMenuItem.Text = "Liberate &Visible Books: {0}"; + this.liberateVisible2ToolStripMenuItem.Click += new System.EventHandler(this.liberateVisible); + // + // exportToolStripMenuItem + // + this.exportToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.exportLibraryToolStripMenuItem}); - this.exportToolStripMenuItem.Name = "exportToolStripMenuItem"; - this.exportToolStripMenuItem.Size = new System.Drawing.Size(53, 20); - this.exportToolStripMenuItem.Text = "E&xport"; - // - // exportLibraryToolStripMenuItem - // - this.exportLibraryToolStripMenuItem.Name = "exportLibraryToolStripMenuItem"; - this.exportLibraryToolStripMenuItem.Size = new System.Drawing.Size(156, 22); - this.exportLibraryToolStripMenuItem.Text = "E&xport Library..."; - this.exportLibraryToolStripMenuItem.Click += new System.EventHandler(this.exportLibraryToolStripMenuItem_Click); - // - // quickFiltersToolStripMenuItem - // - this.quickFiltersToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.exportToolStripMenuItem.Name = "exportToolStripMenuItem"; + this.exportToolStripMenuItem.Size = new System.Drawing.Size(53, 20); + this.exportToolStripMenuItem.Text = "E&xport"; + // + // exportLibraryToolStripMenuItem + // + this.exportLibraryToolStripMenuItem.Name = "exportLibraryToolStripMenuItem"; + this.exportLibraryToolStripMenuItem.Size = new System.Drawing.Size(156, 22); + this.exportLibraryToolStripMenuItem.Text = "E&xport Library..."; + this.exportLibraryToolStripMenuItem.Click += new System.EventHandler(this.exportLibraryToolStripMenuItem_Click); + // + // quickFiltersToolStripMenuItem + // + this.quickFiltersToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.firstFilterIsDefaultToolStripMenuItem, this.editQuickFiltersToolStripMenuItem, this.toolStripSeparator1}); - this.quickFiltersToolStripMenuItem.Name = "quickFiltersToolStripMenuItem"; - this.quickFiltersToolStripMenuItem.Size = new System.Drawing.Size(84, 20); - this.quickFiltersToolStripMenuItem.Text = "Quick &Filters"; - // - // firstFilterIsDefaultToolStripMenuItem - // - this.firstFilterIsDefaultToolStripMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; - this.firstFilterIsDefaultToolStripMenuItem.Name = "firstFilterIsDefaultToolStripMenuItem"; - this.firstFilterIsDefaultToolStripMenuItem.Size = new System.Drawing.Size(256, 22); - this.firstFilterIsDefaultToolStripMenuItem.Text = "Start Libation with 1st filter &Default"; - this.firstFilterIsDefaultToolStripMenuItem.Click += new System.EventHandler(this.firstFilterIsDefaultToolStripMenuItem_Click); - // - // editQuickFiltersToolStripMenuItem - // - this.editQuickFiltersToolStripMenuItem.Name = "editQuickFiltersToolStripMenuItem"; - this.editQuickFiltersToolStripMenuItem.Size = new System.Drawing.Size(256, 22); - this.editQuickFiltersToolStripMenuItem.Text = "&Edit quick filters..."; - this.editQuickFiltersToolStripMenuItem.Click += new System.EventHandler(this.editQuickFiltersToolStripMenuItem_Click); - // - // toolStripSeparator1 - // - this.toolStripSeparator1.Name = "toolStripSeparator1"; - this.toolStripSeparator1.Size = new System.Drawing.Size(253, 6); - // - // scanningToolStripMenuItem - // - this.scanningToolStripMenuItem.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; - this.scanningToolStripMenuItem.Enabled = false; - this.scanningToolStripMenuItem.Image = global::LibationWinForms.Properties.Resources.import_16x16; - this.scanningToolStripMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; - this.scanningToolStripMenuItem.Name = "scanningToolStripMenuItem"; - this.scanningToolStripMenuItem.Size = new System.Drawing.Size(93, 20); - this.scanningToolStripMenuItem.Text = "Scanning..."; - this.scanningToolStripMenuItem.Visible = false; - // - // visibleBooksToolStripMenuItem - // - this.visibleBooksToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.quickFiltersToolStripMenuItem.Name = "quickFiltersToolStripMenuItem"; + this.quickFiltersToolStripMenuItem.Size = new System.Drawing.Size(84, 20); + this.quickFiltersToolStripMenuItem.Text = "Quick &Filters"; + // + // firstFilterIsDefaultToolStripMenuItem + // + this.firstFilterIsDefaultToolStripMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.firstFilterIsDefaultToolStripMenuItem.Name = "firstFilterIsDefaultToolStripMenuItem"; + this.firstFilterIsDefaultToolStripMenuItem.Size = new System.Drawing.Size(256, 22); + this.firstFilterIsDefaultToolStripMenuItem.Text = "Start Libation with 1st filter &Default"; + this.firstFilterIsDefaultToolStripMenuItem.Click += new System.EventHandler(this.firstFilterIsDefaultToolStripMenuItem_Click); + // + // editQuickFiltersToolStripMenuItem + // + this.editQuickFiltersToolStripMenuItem.Name = "editQuickFiltersToolStripMenuItem"; + this.editQuickFiltersToolStripMenuItem.Size = new System.Drawing.Size(256, 22); + this.editQuickFiltersToolStripMenuItem.Text = "&Edit quick filters..."; + this.editQuickFiltersToolStripMenuItem.Click += new System.EventHandler(this.editQuickFiltersToolStripMenuItem_Click); + // + // toolStripSeparator1 + // + this.toolStripSeparator1.Name = "toolStripSeparator1"; + this.toolStripSeparator1.Size = new System.Drawing.Size(253, 6); + // + // scanningToolStripMenuItem + // + this.scanningToolStripMenuItem.Alignment = System.Windows.Forms.ToolStripItemAlignment.Right; + this.scanningToolStripMenuItem.Enabled = false; + this.scanningToolStripMenuItem.Image = global::LibationWinForms.Properties.Resources.import_16x16; + this.scanningToolStripMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; + this.scanningToolStripMenuItem.Name = "scanningToolStripMenuItem"; + this.scanningToolStripMenuItem.Size = new System.Drawing.Size(93, 20); + this.scanningToolStripMenuItem.Text = "Scanning..."; + this.scanningToolStripMenuItem.Visible = false; + // + // visibleBooksToolStripMenuItem + // + this.visibleBooksToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.liberateVisibleToolStripMenuItem, this.replaceTagsToolStripMenuItem, this.setDownloadedToolStripMenuItem, this.removeToolStripMenuItem}); - this.visibleBooksToolStripMenuItem.FormatText = "&Visible Books: {0}"; - this.visibleBooksToolStripMenuItem.Name = "visibleBooksToolStripMenuItem"; - this.visibleBooksToolStripMenuItem.Size = new System.Drawing.Size(108, 20); - this.visibleBooksToolStripMenuItem.Text = "&Visible Books: {0}"; - // - // liberateVisibleToolStripMenuItem - // - this.liberateVisibleToolStripMenuItem.FormatText = "&Liberate: {0}"; - this.liberateVisibleToolStripMenuItem.Name = "liberateVisibleToolStripMenuItem"; - this.liberateVisibleToolStripMenuItem.Size = new System.Drawing.Size(209, 22); - this.liberateVisibleToolStripMenuItem.Text = "&Liberate: {0}"; - this.liberateVisibleToolStripMenuItem.Click += new System.EventHandler(this.liberateVisible); - // - // replaceTagsToolStripMenuItem - // - this.replaceTagsToolStripMenuItem.Name = "replaceTagsToolStripMenuItem"; - this.replaceTagsToolStripMenuItem.Size = new System.Drawing.Size(209, 22); - this.replaceTagsToolStripMenuItem.Text = "Replace &Tags..."; - this.replaceTagsToolStripMenuItem.Click += new System.EventHandler(this.replaceTagsToolStripMenuItem_Click); - // - // setDownloadedToolStripMenuItem - // - this.setDownloadedToolStripMenuItem.Name = "setDownloadedToolStripMenuItem"; - this.setDownloadedToolStripMenuItem.Size = new System.Drawing.Size(209, 22); - this.setDownloadedToolStripMenuItem.Text = "Set \'&Downloaded\' status..."; - this.setDownloadedToolStripMenuItem.Click += new System.EventHandler(this.setDownloadedToolStripMenuItem_Click); - // - // removeToolStripMenuItem - // - this.removeToolStripMenuItem.Name = "removeToolStripMenuItem"; - this.removeToolStripMenuItem.Size = new System.Drawing.Size(209, 22); - this.removeToolStripMenuItem.Text = "&Remove from library..."; - this.removeToolStripMenuItem.Click += new System.EventHandler(this.removeToolStripMenuItem_Click); - // - // settingsToolStripMenuItem - // - this.settingsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.visibleBooksToolStripMenuItem.FormatText = "&Visible Books: {0}"; + this.visibleBooksToolStripMenuItem.Name = "visibleBooksToolStripMenuItem"; + this.visibleBooksToolStripMenuItem.Size = new System.Drawing.Size(108, 20); + this.visibleBooksToolStripMenuItem.Text = "&Visible Books: {0}"; + // + // liberateVisibleToolStripMenuItem + // + this.liberateVisibleToolStripMenuItem.FormatText = "&Liberate: {0}"; + this.liberateVisibleToolStripMenuItem.Name = "liberateVisibleToolStripMenuItem"; + this.liberateVisibleToolStripMenuItem.Size = new System.Drawing.Size(209, 22); + this.liberateVisibleToolStripMenuItem.Text = "&Liberate: {0}"; + this.liberateVisibleToolStripMenuItem.Click += new System.EventHandler(this.liberateVisible); + // + // replaceTagsToolStripMenuItem + // + this.replaceTagsToolStripMenuItem.Name = "replaceTagsToolStripMenuItem"; + this.replaceTagsToolStripMenuItem.Size = new System.Drawing.Size(209, 22); + this.replaceTagsToolStripMenuItem.Text = "Replace &Tags..."; + this.replaceTagsToolStripMenuItem.Click += new System.EventHandler(this.replaceTagsToolStripMenuItem_Click); + // + // setDownloadedToolStripMenuItem + // + this.setDownloadedToolStripMenuItem.Name = "setDownloadedToolStripMenuItem"; + this.setDownloadedToolStripMenuItem.Size = new System.Drawing.Size(209, 22); + this.setDownloadedToolStripMenuItem.Text = "Set \'&Downloaded\' status..."; + this.setDownloadedToolStripMenuItem.Click += new System.EventHandler(this.setDownloadedToolStripMenuItem_Click); + // + // removeToolStripMenuItem + // + this.removeToolStripMenuItem.Name = "removeToolStripMenuItem"; + this.removeToolStripMenuItem.Size = new System.Drawing.Size(209, 22); + this.removeToolStripMenuItem.Text = "&Remove from library..."; + this.removeToolStripMenuItem.Click += new System.EventHandler(this.removeToolStripMenuItem_Click); + // + // settingsToolStripMenuItem + // + this.settingsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.accountsToolStripMenuItem, this.basicSettingsToolStripMenuItem, this.toolStripSeparator2, this.aboutToolStripMenuItem}); - this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem"; - this.settingsToolStripMenuItem.Size = new System.Drawing.Size(61, 20); - this.settingsToolStripMenuItem.Text = "&Settings"; - // - // accountsToolStripMenuItem - // - this.accountsToolStripMenuItem.Name = "accountsToolStripMenuItem"; - this.accountsToolStripMenuItem.Size = new System.Drawing.Size(133, 22); - this.accountsToolStripMenuItem.Text = "&Accounts..."; - this.accountsToolStripMenuItem.Click += new System.EventHandler(this.accountsToolStripMenuItem_Click); - // - // basicSettingsToolStripMenuItem - // - this.basicSettingsToolStripMenuItem.Name = "basicSettingsToolStripMenuItem"; - this.basicSettingsToolStripMenuItem.Size = new System.Drawing.Size(133, 22); - this.basicSettingsToolStripMenuItem.Text = "&Settings..."; - this.basicSettingsToolStripMenuItem.Click += new System.EventHandler(this.basicSettingsToolStripMenuItem_Click); - // - // toolStripSeparator2 - // - this.toolStripSeparator2.Name = "toolStripSeparator2"; - this.toolStripSeparator2.Size = new System.Drawing.Size(130, 6); - // - // aboutToolStripMenuItem - // - this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; - this.aboutToolStripMenuItem.Size = new System.Drawing.Size(133, 22); - this.aboutToolStripMenuItem.Text = "A&bout..."; - this.aboutToolStripMenuItem.Click += new System.EventHandler(this.aboutToolStripMenuItem_Click); - // - // statusStrip1 - // - this.statusStrip1.ImageScalingSize = new System.Drawing.Size(40, 40); - this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem"; + this.settingsToolStripMenuItem.Size = new System.Drawing.Size(61, 20); + this.settingsToolStripMenuItem.Text = "&Settings"; + // + // accountsToolStripMenuItem + // + this.accountsToolStripMenuItem.Name = "accountsToolStripMenuItem"; + this.accountsToolStripMenuItem.Size = new System.Drawing.Size(133, 22); + this.accountsToolStripMenuItem.Text = "&Accounts..."; + this.accountsToolStripMenuItem.Click += new System.EventHandler(this.accountsToolStripMenuItem_Click); + // + // basicSettingsToolStripMenuItem + // + this.basicSettingsToolStripMenuItem.Name = "basicSettingsToolStripMenuItem"; + this.basicSettingsToolStripMenuItem.Size = new System.Drawing.Size(133, 22); + this.basicSettingsToolStripMenuItem.Text = "&Settings..."; + this.basicSettingsToolStripMenuItem.Click += new System.EventHandler(this.basicSettingsToolStripMenuItem_Click); + // + // toolStripSeparator2 + // + this.toolStripSeparator2.Name = "toolStripSeparator2"; + this.toolStripSeparator2.Size = new System.Drawing.Size(130, 6); + // + // aboutToolStripMenuItem + // + this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem"; + this.aboutToolStripMenuItem.Size = new System.Drawing.Size(133, 22); + this.aboutToolStripMenuItem.Text = "A&bout..."; + this.aboutToolStripMenuItem.Click += new System.EventHandler(this.aboutToolStripMenuItem_Click); + // + // statusStrip1 + // + this.statusStrip1.ImageScalingSize = new System.Drawing.Size(40, 40); + this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.visibleCountLbl, this.springLbl, this.backupsCountsLbl, this.pdfsCountsLbl}); - 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.TabIndex = 6; - this.statusStrip1.Text = "statusStrip1"; - // - // visibleCountLbl - // - this.visibleCountLbl.FormatText = "Visible: {0}"; - this.visibleCountLbl.Name = "visibleCountLbl"; - this.visibleCountLbl.Size = new System.Drawing.Size(61, 17); - this.visibleCountLbl.Text = "Visible: {0}"; - // - // springLbl - // - this.springLbl.Name = "springLbl"; - this.springLbl.Size = new System.Drawing.Size(379, 17); - this.springLbl.Spring = true; - // - // backupsCountsLbl - // - this.backupsCountsLbl.Name = "backupsCountsLbl"; - this.backupsCountsLbl.Size = new System.Drawing.Size(218, 17); - this.backupsCountsLbl.Text = "[Calculating backed up book quantities]"; - // - // pdfsCountsLbl - // - this.pdfsCountsLbl.FormatText = "| PDFs: NOT d/l\'ed: {0} Downloaded: {1}"; - this.pdfsCountsLbl.Name = "pdfsCountsLbl"; - this.pdfsCountsLbl.Size = new System.Drawing.Size(218, 17); - this.pdfsCountsLbl.Text = "| PDFs: NOT d/l\'ed: {0} Downloaded: {1}"; - // - // addQuickFilterBtn - // - this.addQuickFilterBtn.Location = new System.Drawing.Point(50, 3); - this.addQuickFilterBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.addQuickFilterBtn.Name = "addQuickFilterBtn"; - this.addQuickFilterBtn.Size = new System.Drawing.Size(137, 27); - this.addQuickFilterBtn.TabIndex = 4; - this.addQuickFilterBtn.Text = "Add To Quick Filters"; - this.addQuickFilterBtn.UseVisualStyleBackColor = true; - this.addQuickFilterBtn.Click += new System.EventHandler(this.addQuickFilterBtn_Click); - // - // splitContainer1 - // - this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; - this.splitContainer1.Location = new System.Drawing.Point(0, 0); - this.splitContainer1.Name = "splitContainer1"; - // - // splitContainer1.Panel1 - // - this.splitContainer1.Panel1.Controls.Add(this.panel1); - this.splitContainer1.Panel1.Controls.Add(this.menuStrip1); - this.splitContainer1.Panel1.Controls.Add(this.statusStrip1); - // - // splitContainer1.Panel2 - // - this.splitContainer1.Panel2.Controls.Add(this.processBookQueue1); - this.splitContainer1.Size = new System.Drawing.Size(1231, 640); - this.splitContainer1.SplitterDistance = 893; - this.splitContainer1.SplitterWidth = 8; - this.splitContainer1.TabIndex = 7; - // - // panel1 - // - this.panel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; - this.panel1.Controls.Add(this.toggleQueueHideBtn); - this.panel1.Controls.Add(this.gridPanel); - this.panel1.Controls.Add(this.addQuickFilterBtn); - this.panel1.Controls.Add(this.filterHelpBtn); - this.panel1.Controls.Add(this.filterSearchTb); - this.panel1.Controls.Add(this.filterBtn); - this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; - 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.TabIndex = 7; - // - // 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.Margin = new System.Windows.Forms.Padding(4, 3, 15, 3); - this.toggleQueueHideBtn.Name = "toggleQueueHideBtn"; - this.toggleQueueHideBtn.Size = new System.Drawing.Size(33, 27); - this.toggleQueueHideBtn.TabIndex = 8; - this.toggleQueueHideBtn.Text = "❱❱❱"; - this.toggleQueueHideBtn.UseVisualStyleBackColor = true; - this.toggleQueueHideBtn.Click += new System.EventHandler(this.ToggleQueueHideBtn_Click); - // - // processBookQueue1 - // - this.processBookQueue1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.processBookQueue1.Dock = System.Windows.Forms.DockStyle.Fill; - 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.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.Controls.Add(this.splitContainer1); - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.MainMenuStrip = this.menuStrip1; - this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.Name = "Form1"; - this.Text = "Libation: Liberate your Library"; - this.Load += new System.EventHandler(this.Form1_Load); - this.menuStrip1.ResumeLayout(false); - this.menuStrip1.PerformLayout(); - this.statusStrip1.ResumeLayout(false); - this.statusStrip1.PerformLayout(); - this.splitContainer1.Panel1.ResumeLayout(false); - this.splitContainer1.Panel1.PerformLayout(); - this.splitContainer1.Panel2.ResumeLayout(false); - ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); - this.splitContainer1.ResumeLayout(false); - this.panel1.ResumeLayout(false); - this.panel1.PerformLayout(); - this.ResumeLayout(false); + 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.TabIndex = 6; + this.statusStrip1.Text = "statusStrip1"; + // + // visibleCountLbl + // + this.visibleCountLbl.FormatText = "Visible: {0}"; + this.visibleCountLbl.Name = "visibleCountLbl"; + this.visibleCountLbl.Size = new System.Drawing.Size(61, 17); + this.visibleCountLbl.Text = "Visible: {0}"; + // + // springLbl + // + this.springLbl.Name = "springLbl"; + this.springLbl.Size = new System.Drawing.Size(379, 17); + this.springLbl.Spring = true; + // + // backupsCountsLbl + // + this.backupsCountsLbl.Name = "backupsCountsLbl"; + this.backupsCountsLbl.Size = new System.Drawing.Size(218, 17); + this.backupsCountsLbl.Text = "[Calculating backed up book quantities]"; + // + // pdfsCountsLbl + // + this.pdfsCountsLbl.FormatText = "| PDFs: NOT d/l\'ed: {0} Downloaded: {1}"; + this.pdfsCountsLbl.Name = "pdfsCountsLbl"; + this.pdfsCountsLbl.Size = new System.Drawing.Size(218, 17); + this.pdfsCountsLbl.Text = "| PDFs: NOT d/l\'ed: {0} Downloaded: {1}"; + // + // addQuickFilterBtn + // + this.addQuickFilterBtn.Location = new System.Drawing.Point(50, 3); + this.addQuickFilterBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + this.addQuickFilterBtn.Name = "addQuickFilterBtn"; + this.addQuickFilterBtn.Size = new System.Drawing.Size(137, 27); + this.addQuickFilterBtn.TabIndex = 4; + this.addQuickFilterBtn.Text = "Add To Quick Filters"; + this.addQuickFilterBtn.UseVisualStyleBackColor = true; + this.addQuickFilterBtn.Click += new System.EventHandler(this.addQuickFilterBtn_Click); + // + // splitContainer1 + // + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.Controls.Add(this.panel1); + this.splitContainer1.Panel1.Controls.Add(this.menuStrip1); + this.splitContainer1.Panel1.Controls.Add(this.statusStrip1); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.processBookQueue1); + this.splitContainer1.Size = new System.Drawing.Size(1231, 640); + this.splitContainer1.SplitterDistance = 893; + this.splitContainer1.SplitterWidth = 8; + this.splitContainer1.TabIndex = 7; + // + // panel1 + // + this.panel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.panel1.Controls.Add(this.productsDisplay); + this.panel1.Controls.Add(this.toggleQueueHideBtn); + this.panel1.Controls.Add(this.addQuickFilterBtn); + this.panel1.Controls.Add(this.filterHelpBtn); + this.panel1.Controls.Add(this.filterSearchTb); + this.panel1.Controls.Add(this.filterBtn); + this.panel1.Dock = System.Windows.Forms.DockStyle.Fill; + 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.TabIndex = 7; + // + // productsDisplay + // + this.productsDisplay.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.productsDisplay.AutoScroll = true; + 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.TabIndex = 9; + this.productsDisplay.LiberateClicked += new System.EventHandler(this.ProductsDisplay_LiberateClicked); + this.productsDisplay.VisibleCountChanged += new System.EventHandler(this.productsDisplay_VisibleCountChanged); + 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.Margin = new System.Windows.Forms.Padding(4, 3, 15, 3); + this.toggleQueueHideBtn.Name = "toggleQueueHideBtn"; + this.toggleQueueHideBtn.Size = new System.Drawing.Size(33, 27); + this.toggleQueueHideBtn.TabIndex = 8; + this.toggleQueueHideBtn.Text = "❱❱❱"; + this.toggleQueueHideBtn.UseVisualStyleBackColor = true; + this.toggleQueueHideBtn.Click += new System.EventHandler(this.ToggleQueueHideBtn_Click); + // + // processBookQueue1 + // + this.processBookQueue1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.processBookQueue1.Dock = System.Windows.Forms.DockStyle.Fill; + 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.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.Controls.Add(this.splitContainer1); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); + this.MainMenuStrip = this.menuStrip1; + this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + this.Name = "Form1"; + this.Text = "Libation: Liberate your Library"; + this.Load += new System.EventHandler(this.Form1_Load); + this.menuStrip1.ResumeLayout(false); + this.menuStrip1.PerformLayout(); + this.statusStrip1.ResumeLayout(false); + this.statusStrip1.PerformLayout(); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel1.PerformLayout(); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.panel1.ResumeLayout(false); + this.panel1.PerformLayout(); + this.ResumeLayout(false); } #endregion - - private System.Windows.Forms.Panel gridPanel; private System.Windows.Forms.MenuStrip menuStrip1; private System.Windows.Forms.ToolStripMenuItem importToolStripMenuItem; private System.Windows.Forms.StatusStrip statusStrip1; @@ -581,5 +583,6 @@ private LibationWinForms.ProcessQueue.ProcessQueueControl processBookQueue1; private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Button toggleQueueHideBtn; + private ProductsDisplay productsDisplay; } } diff --git a/Source/LibationWinForms/Form1.Filter.cs b/Source/LibationWinForms/Form1.Filter.cs index 03e89f9d..2a175e74 100644 --- a/Source/LibationWinForms/Form1.Filter.cs +++ b/Source/LibationWinForms/Form1.Filter.cs @@ -30,7 +30,7 @@ namespace LibationWinForms try { - productsGrid.Filter(filterString); + productsDisplay.Filter(filterString); lastGoodFilter = filterString; } catch (Exception ex) diff --git a/Source/LibationWinForms/Form1.ProcessQueue.cs b/Source/LibationWinForms/Form1.ProcessQueue.cs index 74d30406..8a8178f2 100644 --- a/Source/LibationWinForms/Form1.ProcessQueue.cs +++ b/Source/LibationWinForms/Form1.ProcessQueue.cs @@ -14,14 +14,13 @@ namespace LibationWinForms int WidthChange = 0; private void Configure_ProcessQueue() { - productsGrid.LiberateClicked += ProductsGrid_LiberateClicked; processBookQueue1.popoutBtn.Click += ProcessBookQueue1_PopOut; var coppalseState = Configuration.Instance.GetNonString(nameof(splitContainer1.Panel2Collapsed)); WidthChange = splitContainer1.Panel2.Width + splitContainer1.SplitterWidth; SetQueueCollapseState(coppalseState); } - private void ProductsGrid_LiberateClicked(object sender, LibraryBook e) + private void ProductsDisplay_LiberateClicked(object sender, LibraryBook e) { if (e.Book.UserDefinedItem.BookStatus != LiberatedStatus.Liberated) { diff --git a/Source/LibationWinForms/Form1.QuickFilters.cs b/Source/LibationWinForms/Form1.QuickFilters.cs index 8804f083..3908290b 100644 --- a/Source/LibationWinForms/Form1.QuickFilters.cs +++ b/Source/LibationWinForms/Form1.QuickFilters.cs @@ -14,12 +14,6 @@ namespace LibationWinForms Load += updateFiltersMenu; QuickFilters.UseDefaultChanged += updateFirstFilterIsDefaultToolStripMenuItem; QuickFilters.Updated += updateFiltersMenu; - - productsGrid.InitialLoaded += (_, __) => - { - if (QuickFilters.UseDefault) - performFilter(QuickFilters.Filters.FirstOrDefault()); - }; } private object quickFilterTag { get; } = new(); @@ -56,5 +50,11 @@ namespace LibationWinForms private void addQuickFilterBtn_Click(object sender, EventArgs e) => QuickFilters.Add(this.filterSearchTb.Text); private void editQuickFiltersToolStripMenuItem_Click(object sender, EventArgs e) => new EditQuickFilters().ShowDialog(); + + private void productsDisplay_InitialLoaded(object sender, EventArgs e) + { + if (QuickFilters.UseDefault) + performFilter(QuickFilters.Filters.FirstOrDefault()); + } } } diff --git a/Source/LibationWinForms/Form1.VisibleBooks.cs b/Source/LibationWinForms/Form1.VisibleBooks.cs index 65dfecca..a17effa7 100644 --- a/Source/LibationWinForms/Form1.VisibleBooks.cs +++ b/Source/LibationWinForms/Form1.VisibleBooks.cs @@ -17,28 +17,16 @@ namespace LibationWinForms liberateVisibleToolStripMenuItem.Format(0); liberateVisible2ToolStripMenuItem.Format(0); - // bottom-left visible count - productsGrid.VisibleCountChanged += (_, qty) => visibleCountLbl.Format(qty); - // top menu strip visibleBooksToolStripMenuItem.Format(0); - productsGrid.VisibleCountChanged += (_, qty) => { - visibleBooksToolStripMenuItem.Format(qty); - visibleBooksToolStripMenuItem.Enabled = qty > 0; - var notLiberatedCount = productsGrid.GetVisible().Count(lb => lb.Book.UserDefinedItem.BookStatus == DataLayer.LiberatedStatus.NotLiberated); - }; - - productsGrid.VisibleCountChanged += setLiberatedVisibleMenuItemAsync; LibraryCommands.BookUserDefinedItemCommitted += setLiberatedVisibleMenuItemAsync; } - private async void setLiberatedVisibleMenuItemAsync(object _, int __) - => await Task.Run(setLiberatedVisibleMenuItem); private async void setLiberatedVisibleMenuItemAsync(object _, EventArgs __) => await Task.Run(setLiberatedVisibleMenuItem); void setLiberatedVisibleMenuItem() { - var notLiberated = productsGrid.GetVisible().Count(lb => lb.Book.UserDefinedItem.BookStatus == DataLayer.LiberatedStatus.NotLiberated); + var notLiberated = productsDisplay.GetVisible().Count(lb => lb.Book.UserDefinedItem.BookStatus == DataLayer.LiberatedStatus.NotLiberated); this.UIThreadSync(() => { if (notLiberated > 0) @@ -63,7 +51,7 @@ namespace LibationWinForms private async void liberateVisible(object sender, EventArgs e) { SetQueueCollapseState(false); - await Task.Run(() => processBookQueue1.AddDownloadDecrypt(productsGrid.GetVisible())); + await Task.Run(() => processBookQueue1.AddDownloadDecrypt(productsDisplay.GetVisible())); } private void replaceTagsToolStripMenuItem_Click(object sender, EventArgs e) @@ -73,7 +61,7 @@ namespace LibationWinForms if (result != DialogResult.OK) return; - var visibleLibraryBooks = productsGrid.GetVisible(); + var visibleLibraryBooks = productsDisplay.GetVisible(); var confirmationResult = MessageBoxLib.ShowConfirmationDialog( visibleLibraryBooks, @@ -95,7 +83,7 @@ namespace LibationWinForms if (result != DialogResult.OK) return; - var visibleLibraryBooks = productsGrid.GetVisible(); + var visibleLibraryBooks = productsDisplay.GetVisible(); var confirmationResult = MessageBoxLib.ShowConfirmationDialog( visibleLibraryBooks, @@ -112,7 +100,7 @@ namespace LibationWinForms private async void removeToolStripMenuItem_Click(object sender, EventArgs e) { - var visibleLibraryBooks = productsGrid.GetVisible(); + var visibleLibraryBooks = productsDisplay.GetVisible(); var confirmationResult = MessageBoxLib.ShowConfirmationDialog( visibleLibraryBooks, @@ -125,5 +113,20 @@ namespace LibationWinForms var visibleIds = visibleLibraryBooks.Select(lb => lb.Book.AudibleProductId).ToList(); await LibraryCommands.RemoveBooksAsync(visibleIds); } + + private async void productsDisplay_VisibleCountChanged(object sender, int qty) + { + // bottom-left visible count + visibleCountLbl.Format(qty); + + // top menu strip + visibleBooksToolStripMenuItem.Format(qty); + visibleBooksToolStripMenuItem.Enabled = qty > 0; + + //Not used for anything? + var notLiberatedCount = productsDisplay.GetVisible().Count(lb => lb.Book.UserDefinedItem.BookStatus == DataLayer.LiberatedStatus.NotLiberated); + + await Task.Run(setLiberatedVisibleMenuItem); + } } } diff --git a/Source/LibationWinForms/Form1.cs b/Source/LibationWinForms/Form1.cs index 0c2f3ce1..43151f73 100644 --- a/Source/LibationWinForms/Form1.cs +++ b/Source/LibationWinForms/Form1.cs @@ -12,24 +12,10 @@ namespace LibationWinForms { public partial class Form1 : Form { - private ProductsDisplay productsGrid { get; } - public Form1() { InitializeComponent(); - if (this.DesignMode) - return; - - { - // I'd actually like these lines to be handled in the designer, but I'm currently getting this error when I try: - // Failed to create component 'ProductsGrid'. The error message follows: - // 'Microsoft.DotNet.DesignTools.Client.DesignToolsServerException: Object reference not set to an instance of an object. - // Since the designer's choking on it, I'm keeping it below the DesignMode check to be safe - productsGrid = new ProductsDisplay { Dock = DockStyle.Fill }; - gridPanel.Controls.Add(productsGrid); - } - // Pre-requisite: // Before calling anything else, including subscribing to events, ensure database exists. If we wait and let it happen lazily, race conditions and errors are likely during new installs using var _ = DbContexts.GetContext(); @@ -67,8 +53,8 @@ namespace LibationWinForms // Configure_Grid(); // since it's just this, can keep here. If it needs more, then give grid it's own 'partial class Form1' { - this.Load += (_, __) => productsGrid.Display(); - LibraryCommands.LibrarySizeChanged += (_, __) => this.UIThreadAsync(() => productsGrid.Display()); + this.Load += (_, __) => productsDisplay.Display(); + LibraryCommands.LibrarySizeChanged += (_, __) => this.UIThreadAsync(() => productsDisplay.Display()); } } diff --git a/Source/LibationWinForms/LibationWinForms.csproj b/Source/LibationWinForms/LibationWinForms.csproj index 979db0ff..2ecd019b 100644 --- a/Source/LibationWinForms/LibationWinForms.csproj +++ b/Source/LibationWinForms/LibationWinForms.csproj @@ -45,9 +45,6 @@
- - UserControl - True True @@ -56,9 +53,6 @@ - - Designer - ResXFileCodeGenerator Resources.Designer.cs diff --git a/Source/LibationWinForms/grid/EditTagsDataGridViewImageButtonColumn.cs b/Source/LibationWinForms/grid/EditTagsDataGridViewImageButtonColumn.cs index 1349325a..db8bb484 100644 --- a/Source/LibationWinForms/grid/EditTagsDataGridViewImageButtonColumn.cs +++ b/Source/LibationWinForms/grid/EditTagsDataGridViewImageButtonColumn.cs @@ -1,4 +1,5 @@ -using System.Drawing; +using Dinah.Core.Windows.Forms; +using System.Drawing; using System.Windows.Forms; namespace LibationWinForms @@ -18,6 +19,12 @@ namespace LibationWinForms 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) { + if (rowIndex >= 0 && DataGridView.GetBoundItem(rowIndex) is SeriesEntry) + { + base.Paint(graphics, clipBounds, cellBounds, rowIndex, DataGridViewElementStates.None, null, null, null, cellStyle, advancedBorderStyle, DataGridViewPaintParts.Background | DataGridViewPaintParts.Border); + return; + } + var tagsString = (string)value; var foreColor = tagsString?.Contains("hidden") == true ? HiddenForeColor : DataGridView.DefaultCellStyle.ForeColor; diff --git a/Source/LibationWinForms/grid/FilterableSortableBindingList.cs b/Source/LibationWinForms/grid/FilterableSortableBindingList.cs index e3bcaad0..e309f7fe 100644 --- a/Source/LibationWinForms/grid/FilterableSortableBindingList.cs +++ b/Source/LibationWinForms/grid/FilterableSortableBindingList.cs @@ -8,17 +8,17 @@ using System.Linq; namespace LibationWinForms { /* - * Allows filtering of the underlying SortableBindingList - * by implementing IBindingListView and using SearchEngineCommands - * - * When filtering is applied, the filtered-out items are removed - * from the base list and added to the private FilterRemoved list. - * When filtering is removed, items in the FilterRemoved list are - * added back to the base list. - * - * Remove is overridden to ensure that removed items are removed from - * the base list (visible items) as well as the FilterRemoved list. - */ + * Allows filtering of the underlying SortableBindingList + * by implementing IBindingListView and using SearchEngineCommands + * + * When filtering is applied, the filtered-out items are removed + * from the base list and added to the private FilterRemoved list. + * When filtering is removed, items in the FilterRemoved list are + * added back to the base list. + * + * 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 { /// @@ -50,14 +50,7 @@ namespace LibationWinForms } /// All items in the list, including those filtered out. - public List AllItems() - { - var allItems = Items.Concat(FilterRemoved); - - var series = allItems.Where(i => i is SeriesEntry).Cast().SelectMany(s => s.Children); - - return series.Concat(allItems).ToList(); - } + public List AllItems() => Items.Concat(FilterRemoved).ToList(); private void ApplyFilter(string filterString) { @@ -65,32 +58,41 @@ namespace LibationWinForms RemoveFilter(); FilterString = filterString; - SearchResults = SearchEngineCommands.Search(filterString); - var filteredOut = Items.Where(i => i is LibraryBookEntry).Cast().ExceptBy(SearchResults.Docs.Select(d => d.ProductId), ge => ge.AudibleProductId).Cast().ToList(); - var parents = Items.Where(i => i is SeriesEntry).Cast(); + var booksFilteredIn = Items.LibraryBooks().Join(SearchResults.Docs, lbe => lbe.AudibleProductId, d => d.ProductId, (lbe, d) => (GridEntry)lbe); - foreach (var p in parents) + //Find all series containing children that match the search criteria + var seriesFilteredIn = Items.Series().Where(s => s.Children.Join(SearchResults.Docs, lbe => lbe.AudibleProductId, d => d.ProductId, (lbe, d) => lbe).Any()); + + var filteredOut = Items.Except(booksFilteredIn.Concat(seriesFilteredIn)).ToList(); + + foreach (var item in filteredOut) { - if (p.Children.Cast().ExceptBy(SearchResults.Docs.Select(d => d.ProductId), ge => ge.AudibleProductId).Count() == p.Children.Count) - { - //Don't show series whose episodes have all been filtered out - filteredOut.Add(p); - } + FilterRemoved.Add(item); + base.Remove(item); } + } - for (int i = 0; i < filteredOut.Count; i++) - { - FilterRemoved.Add(filteredOut[i]); - base.Remove(filteredOut[i]); - } + public void CollapseAll() + { + foreach (var series in Items.Series().ToList()) + CollapseItem(series); + } + + public void ExpandAll() + { + foreach (var series in Items.Series().ToList()) + ExpandItem(series); } public void CollapseItem(SeriesEntry sEntry) { - foreach (var item in Items.Where(b => b is LibraryBookEntry).Cast().Where(b => b.Parent == sEntry).ToList()) - base.Remove(item); + foreach (var episode in Items.Where(b => b.Parent == sEntry).Cast().ToList()) + { + FilterRemoved.Add(episode); + base.Remove(episode); + } sEntry.Liberate.Expanded = false; } @@ -98,16 +100,17 @@ namespace LibationWinForms public void ExpandItem(SeriesEntry sEntry) { var sindex = Items.IndexOf(sEntry); - var children = sEntry.Children.Cast().ToList(); - for (int i = 0; i < children.Count; i++) + + foreach (var episode in FilterRemoved.Where(b => b.Parent == sEntry).Cast().ToList()) { - if (SearchResults is null || SearchResults.Docs.Any(d=> d.ProductId == children[i].AudibleProductId)) - Insert(++sindex, children[i]); - else + if (SearchResults is null || SearchResults.Docs.Any(d => d.ProductId == episode.AudibleProductId)) { - FilterRemoved.Add(children[i]); + FilterRemoved.Remove(episode); + Items.Insert(++sindex, episode); } } + + OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); sEntry.Liberate.Expanded = true; } @@ -116,13 +119,15 @@ namespace LibationWinForms if (FilterString is null) return; int visibleCount = Items.Count; - for (int i = 0; i < FilterRemoved.Count; i++) - { - if (FilterRemoved[i].Parent is null || FilterRemoved[i].Parent.Liberate.Expanded) - base.InsertItem(i + visibleCount, FilterRemoved[i]); - } - FilterRemoved.Clear(); + foreach (var item in FilterRemoved.ToList()) + { + if (item.Parent is null || item.Parent.Liberate.Expanded) + { + FilterRemoved.Remove(item); + base.InsertItem(visibleCount++, item); + } + } if (IsSortedCore) Sort(); diff --git a/Source/LibationWinForms/grid/GridEntry.cs b/Source/LibationWinForms/grid/GridEntry.cs index a9d86b41..e0f19c1c 100644 --- a/Source/LibationWinForms/grid/GridEntry.cs +++ b/Source/LibationWinForms/grid/GridEntry.cs @@ -5,25 +5,37 @@ using LibationFileManager; using System; using System.Collections; using System.Collections.Generic; -using System.ComponentModel; -using System.Drawing; +using System.ComponentModel;using System.Drawing; +using System.Linq; namespace LibationWinForms { public interface IHierarchical where T : class { T Parent { get; } - List Children { get; } } - internal class LiberateStatus + + public class LiberateStatus : IComparable { public LiberatedStatus BookStatus; public LiberatedStatus? PdfStatus; public bool IsSeries; public bool Expanded; + + public int CompareTo(object obj) + { + if (obj is not LiberateStatus 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); + } } - internal abstract class GridEntry : AsyncNotifyPropertyChanged, IMemberComparable, IHierarchical + public abstract class GridEntry : AsyncNotifyPropertyChanged, IMemberComparable, IHierarchical { protected abstract Book Book { get; } @@ -38,11 +50,15 @@ namespace LibationWinForms NotifyPropertyChanged(); } } + + [Browsable(false)] + public new bool InvokeRequired => base.InvokeRequired; + [Browsable(false)] public GridEntry Parent { get; set; } - public List Children { get; set; } + [Browsable(false)] + public abstract DateTime DateAdded { get; } public abstract string ProductRating { get; protected set; } public abstract string PurchaseDate { get; protected set; } - public abstract DateTime DateAdded { get; } public abstract string MyRating { get; protected set; } public abstract string Series { get; protected set; } public abstract string Title { get; protected set; } @@ -89,12 +105,20 @@ namespace LibationWinForms { typeof(float), new ObjectComparer() }, { typeof(bool), new ObjectComparer() }, { typeof(DateTime), new ObjectComparer() }, - { typeof(LiberatedStatus), new ObjectComparer() }, + { typeof(LiberateStatus), new ObjectComparer() }, }; ~GridEntry() { PictureStorage.PictureCached -= PictureStorage_PictureCached; - } + } + } + + internal static class GridEntryExtensions + { + 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(); } } diff --git a/Source/LibationWinForms/grid/LibraryBookEntry.cs b/Source/LibationWinForms/grid/LibraryBookEntry.cs index e87ae630..c1be718c 100644 --- a/Source/LibationWinForms/grid/LibraryBookEntry.cs +++ b/Source/LibationWinForms/grid/LibraryBookEntry.cs @@ -17,7 +17,7 @@ namespace LibationWinForms /// /// The View Model for a LibraryBook /// - internal class LibraryBookEntry : GridEntry + public class LibraryBookEntry : GridEntry { #region implementation properties NOT exposed to the view // hide from public fields from Data Source GUI with [Browsable(false)] @@ -187,7 +187,7 @@ namespace LibationWinForms { nameof(Category), () => Category }, { nameof(Misc), () => Misc }, { nameof(DisplayTags), () => DisplayTags }, - { nameof(Liberate), () => Liberate.BookStatus }, + { nameof(Liberate), () => Liberate }, { nameof(DateAdded), () => DateAdded }, }; diff --git a/Source/LibationWinForms/grid/MasterDataGridView.cs b/Source/LibationWinForms/grid/MasterDataGridView.cs deleted file mode 100644 index fd3b481a..00000000 --- a/Source/LibationWinForms/grid/MasterDataGridView.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Dinah.Core.Windows.Forms; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace LibationWinForms -{ - - internal class MasterDataGridView : DataGridView - { - internal delegate void LibraryBookEntryClickedEventHandler(DataGridViewCellEventArgs e, LibraryBookEntry entry); - public event LibraryBookEntryClickedEventHandler LibraryBookEntryClicked; - public MasterDataGridView() - { - - } - - - public GridEntry getGridEntry(int rowIndex) => this.GetBoundItem(rowIndex); - - } -} diff --git a/Source/LibationWinForms/grid/ProductsDisplay.Designer.cs b/Source/LibationWinForms/grid/ProductsDisplay.Designer.cs index d15ad85c..a10e1c95 100644 --- a/Source/LibationWinForms/grid/ProductsDisplay.Designer.cs +++ b/Source/LibationWinForms/grid/ProductsDisplay.Designer.cs @@ -28,19 +28,38 @@ /// private void InitializeComponent() { + this.productsGrid = new LibationWinForms.grid.ProductsGrid(); this.SuspendLayout(); // + // productsGrid + // + this.productsGrid.AutoScroll = true; + this.productsGrid.Dock = System.Windows.Forms.DockStyle.Fill; + this.productsGrid.Location = new System.Drawing.Point(0, 0); + this.productsGrid.Name = "productsGrid"; + this.productsGrid.Size = new System.Drawing.Size(1510, 380); + this.productsGrid.TabIndex = 0; + this.productsGrid.LiberateClicked += new LibationWinForms.grid.ProductsGrid.LibraryBookEntryClickedEventHandler(this.productsGrid_LiberateClicked); + this.productsGrid.CoverClicked += new LibationWinForms.grid.ProductsGrid.LibraryBookEntryClickedEventHandler(this.productsGrid_CoverClicked); + this.productsGrid.DetailsClicked += new LibationWinForms.grid.ProductsGrid.LibraryBookEntryClickedEventHandler(this.productsGrid_DetailsClicked); + this.productsGrid.DescriptionClicked += new LibationWinForms.grid.ProductsGrid.LibraryBookEntryRectangleClickedEventHandler(this.productsGrid_DescriptionClicked); + this.productsGrid.VisibleCountChanged += new System.EventHandler(this.productsGrid_VisibleCountChanged); + // // ProductsDisplay // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.productsGrid); this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.Name = "ProductsDisplay"; this.Size = new System.Drawing.Size(1510, 380); + this.Load += new System.EventHandler(this.ProductsDisplay_Load); this.ResumeLayout(false); } #endregion + + private grid.ProductsGrid productsGrid; } } diff --git a/Source/LibationWinForms/grid/ProductsDisplay.cs b/Source/LibationWinForms/grid/ProductsDisplay.cs index e0e0fd58..0a9bee61 100644 --- a/Source/LibationWinForms/grid/ProductsDisplay.cs +++ b/Source/LibationWinForms/grid/ProductsDisplay.cs @@ -6,65 +6,35 @@ using System.Threading.Tasks; using System.Windows.Forms; using ApplicationServices; using DataLayer; -using Dinah.Core.Windows.Forms; using FileLiberator; using LibationFileManager; using LibationWinForms.Dialogs; namespace LibationWinForms { - - #region // legacy instructions to update data_grid_view - // INSTRUCTIONS TO UPDATE DATA_GRID_VIEW - // - delete current DataGridView - // - view > other windows > data sources - // - refresh - // OR - // - Add New Data Source - // Object. Next - // LibationWinForms - // AudibleDTO - // GridEntry - // - go to Design view - // - click on Data Sources > ProductItem. dropdown: DataGridView - // - drag/drop ProductItem on design surface - // - // as of august 2021 this does not work in vs2019 with .net5 projects - // VS has improved since then with .net6+ but I haven't checked again - #endregion - - public partial class ProductsDisplay : UserControl { public event EventHandler LiberateClicked; /// Number of visible rows has changed public event EventHandler VisibleCountChanged; - // alias - - private ProductsGrid grid; - public ProductsDisplay() { InitializeComponent(); + } - grid = new ProductsGrid(); - grid.Dock = DockStyle.Fill; - Controls.Add(grid); - - if (this.DesignMode) + private void ProductsDisplay_Load(object sender, EventArgs e) + { + if (DesignMode) return; - grid.LiberateClicked += (_, book) => LiberateClicked?.Invoke(this, book.LibraryBook); - grid.DetailsClicked += Grid_DetailsClicked; - grid.CoverClicked += Grid_CoverClicked; - grid.DescriptionClicked += Grid_DescriptionClicked1; } + #region Button controls private ImageDisplay imageDisplay; - private async void Grid_CoverClicked(DataGridViewCellEventArgs e, LibraryBookEntry liveGridEntry) + private async void productsGrid_CoverClicked(LibraryBookEntry liveGridEntry) { var picDefinition = new PictureDefinition(liveGridEntry.LibraryBook.Book.PictureLarge ?? liveGridEntry.LibraryBook.Book.PictureId, PictureSize.Native); var picDlTask = Task.Run(() => PictureStorage.GetPictureSynchronously(picDefinition)); @@ -87,7 +57,7 @@ namespace LibationWinForms imageDisplay.CoverPicture = await picDlTask; } - private void Grid_DescriptionClicked1(DataGridViewCellEventArgs e, LibraryBookEntry liveGridEntry, Rectangle cellRectangle) + private void productsGrid_DescriptionClicked(LibraryBookEntry liveGridEntry, Rectangle cellRectangle) { var displayWindow = new DescriptionDisplay { @@ -101,13 +71,13 @@ namespace LibationWinForms displayWindow.Close(); } - grid.Scroll += CloseWindow; - displayWindow.FormClosed += (_, _) => grid.Scroll -= CloseWindow; + productsGrid.Scroll += CloseWindow; + displayWindow.FormClosed += (_, _) => productsGrid.Scroll -= CloseWindow; displayWindow.Show(this); } - private void Grid_DetailsClicked(DataGridViewCellEventArgs e, LibraryBookEntry liveGridEntry) + private void productsGrid_DetailsClicked(LibraryBookEntry liveGridEntry) { var bookDetailsForm = new BookDetailsDialog(liveGridEntry.LibraryBook); if (bookDetailsForm.ShowDialog() == DialogResult.OK) @@ -128,13 +98,12 @@ namespace LibationWinForms if (!hasBeenDisplayed) { // bind - grid.bindToGrid(lib); + productsGrid.BindToGrid(lib); hasBeenDisplayed = true; InitialLoaded?.Invoke(this, new()); - VisibleCountChanged?.Invoke(this, grid.GetVisible().Count()); } else - grid.updateGrid(lib); + productsGrid.UpdateGrid(lib); } @@ -143,10 +112,20 @@ namespace LibationWinForms #region Filter public void Filter(string searchString) - => grid.Filter(searchString); + => productsGrid.Filter(searchString); #endregion - internal List GetVisible() => grid.GetVisible().ToList(); + internal List GetVisible() => productsGrid.GetVisible().Select(v => v.LibraryBook).ToList(); + + private void productsGrid_VisibleCountChanged(object sender, int count) + { + VisibleCountChanged?.Invoke(this, count); + } + + private void productsGrid_LiberateClicked(LibraryBookEntry liveGridEntry) + { + LiberateClicked?.Invoke(this, liveGridEntry.LibraryBook); + } } } diff --git a/Source/LibationWinForms/grid/ProductsGrid.Designer.cs b/Source/LibationWinForms/grid/ProductsGrid.Designer.cs index 27496d79..e818ed5b 100644 --- a/Source/LibationWinForms/grid/ProductsGrid.Designer.cs +++ b/Source/LibationWinForms/grid/ProductsGrid.Designer.cs @@ -1,4 +1,4 @@ -namespace LibationWinForms +namespace LibationWinForms.grid { partial class ProductsGrid { @@ -30,7 +30,6 @@ { this.components = new System.ComponentModel.Container(); System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); - this.gridEntryBindingSource = new LibationWinForms.SyncBindingSource(this.components); this.gridEntryDataGridView = new System.Windows.Forms.DataGridView(); this.liberateGVColumn = new LibationWinForms.LiberateDataGridViewImageButtonColumn(); this.coverGVColumn = new System.Windows.Forms.DataGridViewImageColumn(); @@ -47,39 +46,32 @@ this.miscGVColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.tagAndDetailsGVColumn = new LibationWinForms.EditTagsDataGridViewImageButtonColumn(); this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); - ((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).BeginInit(); this.SuspendLayout(); // - // gridEntryBindingSource - // - this.gridEntryBindingSource.DataSource = typeof(LibationWinForms.GridEntry); - // // gridEntryDataGridView // this.gridEntryDataGridView.AllowUserToAddRows = false; this.gridEntryDataGridView.AllowUserToDeleteRows = false; this.gridEntryDataGridView.AllowUserToOrderColumns = true; this.gridEntryDataGridView.AllowUserToResizeRows = false; - this.gridEntryDataGridView.AutoGenerateColumns = false; this.gridEntryDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.gridEntryDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { - this.liberateGVColumn, - this.coverGVColumn, - this.titleGVColumn, - this.authorsGVColumn, - this.narratorsGVColumn, - this.lengthGVColumn, - this.seriesGVColumn, - this.descriptionGVColumn, - this.categoryGVColumn, - this.productRatingGVColumn, - this.purchaseDateGVColumn, - this.myRatingGVColumn, - this.miscGVColumn, - this.tagAndDetailsGVColumn}); + this.liberateGVColumn, + this.coverGVColumn, + this.titleGVColumn, + this.authorsGVColumn, + this.narratorsGVColumn, + this.lengthGVColumn, + this.seriesGVColumn, + this.descriptionGVColumn, + this.categoryGVColumn, + this.productRatingGVColumn, + this.purchaseDateGVColumn, + this.myRatingGVColumn, + this.miscGVColumn, + this.tagAndDetailsGVColumn}); this.gridEntryDataGridView.ContextMenuStrip = this.contextMenuStrip1; - this.gridEntryDataGridView.DataSource = this.gridEntryBindingSource; dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Window; dataGridViewCellStyle1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); @@ -90,16 +82,15 @@ this.gridEntryDataGridView.DefaultCellStyle = dataGridViewCellStyle1; this.gridEntryDataGridView.Dock = System.Windows.Forms.DockStyle.Fill; this.gridEntryDataGridView.Location = new System.Drawing.Point(0, 0); - this.gridEntryDataGridView.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.gridEntryDataGridView.Name = "gridEntryDataGridView"; this.gridEntryDataGridView.ReadOnly = true; this.gridEntryDataGridView.RowHeadersVisible = false; this.gridEntryDataGridView.RowTemplate.Height = 82; this.gridEntryDataGridView.Size = new System.Drawing.Size(1510, 380); this.gridEntryDataGridView.TabIndex = 0; + this.gridEntryDataGridView.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.DataGridView_CellContentClick); this.gridEntryDataGridView.CellToolTipTextNeeded += new System.Windows.Forms.DataGridViewCellToolTipTextNeededEventHandler(this.gridEntryDataGridView_CellToolTipTextNeeded); this.gridEntryDataGridView.ColumnDisplayIndexChanged += new System.Windows.Forms.DataGridViewColumnEventHandler(this.gridEntryDataGridView_ColumnDisplayIndexChanged); - this.gridEntryDataGridView.ColumnWidthChanged += new System.Windows.Forms.DataGridViewColumnEventHandler(this.gridEntryDataGridView_ColumnWidthChanged); // // liberateGVColumn // @@ -222,19 +213,18 @@ // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.AutoScroll = true; this.Controls.Add(this.gridEntryDataGridView); - this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.Name = "ProductsGrid"; this.Size = new System.Drawing.Size(1510, 380); - ((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).EndInit(); + this.Load += new System.EventHandler(this.ProductsGrid_Load); ((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).EndInit(); this.ResumeLayout(false); } - #endregion - private LibationWinForms.SyncBindingSource gridEntryBindingSource; + #endregion private System.Windows.Forms.DataGridView gridEntryDataGridView; private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; private LiberateDataGridViewImageButtonColumn liberateGVColumn; diff --git a/Source/LibationWinForms/grid/ProductsGrid.cs b/Source/LibationWinForms/grid/ProductsGrid.cs index 2d585f27..b119285d 100644 --- a/Source/LibationWinForms/grid/ProductsGrid.cs +++ b/Source/LibationWinForms/grid/ProductsGrid.cs @@ -1,103 +1,70 @@ -using System; +using DataLayer; +using Dinah.Core.Windows.Forms; +using LibationFileManager; +using System; using System.Collections.Generic; +using System.Data; using System.Drawing; using System.Linq; using System.Windows.Forms; -using ApplicationServices; -using DataLayer; -using Dinah.Core.Windows.Forms; -using LibationFileManager; -namespace LibationWinForms +namespace LibationWinForms.grid { - - #region // legacy instructions to update data_grid_view - // INSTRUCTIONS TO UPDATE DATA_GRID_VIEW - // - delete current DataGridView - // - view > other windows > data sources - // - refresh - // OR - // - Add New Data Source - // Object. Next - // LibationWinForms - // AudibleDTO - // GridEntry - // - go to Design view - // - click on Data Sources > ProductItem. dropdown: DataGridView - // - drag/drop ProductItem on design surface - // - // as of august 2021 this does not work in vs2019 with .net5 projects - // VS has improved since then with .net6+ but I haven't checked again - #endregion - - public partial class ProductsGrid : UserControl { - - internal delegate void LibraryBookEntryClickedEventHandler(DataGridViewCellEventArgs e, LibraryBookEntry liveGridEntry); - internal delegate void LibraryBookEntryRectangleClickedEventHandler(DataGridViewCellEventArgs e, LibraryBookEntry liveGridEntry, Rectangle cellRectangle); - internal event LibraryBookEntryClickedEventHandler LiberateClicked; - internal event LibraryBookEntryClickedEventHandler CoverClicked; - internal event LibraryBookEntryClickedEventHandler DetailsClicked; - internal event LibraryBookEntryRectangleClickedEventHandler DescriptionClicked; - public new event EventHandler Scroll; - - private FilterableSortableBindingList bindingList; + public delegate void LibraryBookEntryClickedEventHandler(LibraryBookEntry liveGridEntry); + public delegate void LibraryBookEntryRectangleClickedEventHandler(LibraryBookEntry liveGridEntry, Rectangle cellRectangle); /// Number of visible rows has changed public event EventHandler VisibleCountChanged; + public event LibraryBookEntryClickedEventHandler LiberateClicked; + public event LibraryBookEntryClickedEventHandler CoverClicked; + public event LibraryBookEntryClickedEventHandler DetailsClicked; + public event LibraryBookEntryRectangleClickedEventHandler DescriptionClicked; + public new event EventHandler Scroll; - // alias - private DataGridView _dataGridView => gridEntryDataGridView; + private FilterableSortableBindingList bindingList; + private SyncBindingSource gridEntryBindingSource; public ProductsGrid() { InitializeComponent(); - - if (this.DesignMode) - return; - EnableDoubleBuffering(); - - _dataGridView.CellContentClick += DataGridView_CellContentClick; - _dataGridView.Scroll += (_, s) => Scroll?.Invoke(this, s); - - Load += ProductsGrid_Load; - } - - private void ProductsGrid_Scroll(object sender, ScrollEventArgs e) - { - throw new NotImplementedException(); + //There a bug in designer that causes errors if you add BindingSource to the DataGridView at design time. + gridEntryBindingSource = new SyncBindingSource(); + gridEntryDataGridView.DataSource = gridEntryBindingSource; + gridEntryDataGridView.Scroll += (_, s) => Scroll?.Invoke(this, s); } private void EnableDoubleBuffering() { - var propertyInfo = _dataGridView.GetType().GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); + var propertyInfo = gridEntryDataGridView.GetType().GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); - propertyInfo.SetValue(_dataGridView, true, null); + propertyInfo.SetValue(gridEntryDataGridView, true, null); } #region Button controls - private void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e) { // handle grid button click: https://stackoverflow.com/a/13687844 - if (e.RowIndex < 0) + if ( e.RowIndex < 0) return; + var column = gridEntryDataGridView.Columns[e.ColumnIndex]; + var entry = getGridEntry(e.RowIndex); if (entry is LibraryBookEntry lbEntry) { - if (e.ColumnIndex == liberateGVColumn.Index) - LiberateClicked?.Invoke(e, lbEntry); - else if (e.ColumnIndex == tagAndDetailsGVColumn.Index && entry is LibraryBookEntry) - DetailsClicked?.Invoke(e, lbEntry); - else if (e.ColumnIndex == descriptionGVColumn.Index) - DescriptionClicked?.Invoke(e, lbEntry, _dataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false)); - else if (e.ColumnIndex == coverGVColumn.Index) - CoverClicked?.Invoke(e, lbEntry); + if (gridEntryDataGridView.Columns[e.ColumnIndex].DataPropertyName == liberateGVColumn.DataPropertyName) + LiberateClicked?.Invoke(lbEntry); + else if (gridEntryDataGridView.Columns[e.ColumnIndex].DataPropertyName == tagAndDetailsGVColumn.DataPropertyName && entry is LibraryBookEntry) + DetailsClicked?.Invoke(lbEntry); + else if (gridEntryDataGridView.Columns[e.ColumnIndex].DataPropertyName == descriptionGVColumn.DataPropertyName) + DescriptionClicked?.Invoke(lbEntry, gridEntryDataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false)); + else if (gridEntryDataGridView.Columns[e.ColumnIndex].DataPropertyName == coverGVColumn.DataPropertyName) + CoverClicked?.Invoke(lbEntry); } - else if (entry is SeriesEntry sEntry && e.ColumnIndex == liberateGVColumn.Index) + else if (entry is SeriesEntry sEntry && gridEntryDataGridView.Columns[e.ColumnIndex].DataPropertyName == liberateGVColumn.DataPropertyName) { if (sEntry.Liberate.Expanded) bindingList.CollapseItem(sEntry); @@ -105,16 +72,19 @@ namespace LibationWinForms bindingList.ExpandItem(sEntry); sEntry.NotifyPropertyChanged(nameof(sEntry.Liberate)); + + VisibleCountChanged?.Invoke(this, bindingList.LibraryBooks().Count()); } } - private GridEntry getGridEntry(int rowIndex) => _dataGridView.GetBoundItem(rowIndex); + private GridEntry getGridEntry(int rowIndex) => gridEntryDataGridView.GetBoundItem(rowIndex); #endregion + #region UI display functions - internal void bindToGrid(List dbBooks) + internal void BindToGrid(List dbBooks) { var geList = dbBooks.Where(b => b.Book.ContentType is not ContentType.Episode).Select(b => new LibraryBookEntry(b)).Cast().ToList(); @@ -125,24 +95,28 @@ namespace LibationWinForms foreach (var s in 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 }).Cast().ToList(); + 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); + geList.Add(seriesEntry); + geList.AddRange(seriesEntry.Children); } - bindingList = new FilterableSortableBindingList(geList.OrderByDescending(ge => ge.DateAdded)); + bindingList = new FilterableSortableBindingList(geList.OrderByDescending(e => e.DateAdded)); + bindingList.CollapseAll(); gridEntryBindingSource.DataSource = bindingList; + VisibleCountChanged?.Invoke(this, bindingList.LibraryBooks().Count()); } - internal void updateGrid(List dbBooks) + internal void UpdateGrid(List dbBooks) { int visibleCount = bindingList.Count; string existingFilter = gridEntryBindingSource.Filter; //Add absent books to grid, or update current books - var allItmes = bindingList.AllItems().Where(i => i is LibraryBookEntry).Cast(); + var allItmes = bindingList.AllItems().LibraryBooks(); for (var i = dbBooks.Count - 1; i >= 0; i--) { var libraryBook = dbBooks[i]; @@ -155,26 +129,31 @@ namespace LibationWinForms if (libraryBook.Book.ContentType is ContentType.Episode) { - //Find the series that libraryBook, if it exists - var series = bindingList.AllItems().Where(i => i is SeriesEntry).Cast().FirstOrDefault(i => libraryBook.Book.SeriesLink.Any(s => s.Series.Name == i.Series)); + //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)); if (series is null) { //Series doesn't exist yet, so create and add it - var newSeries = new SeriesEntry { Children = new List { lb } }; + var newSeries = new SeriesEntry { Children = new List { lb } }; newSeries.setSeriesBook(libraryBook.Book.SeriesLink.First()); lb.Parent = newSeries; newSeries.Liberate.Expanded = true; bindingList.Insert(0, newSeries); + series = newSeries; } else { lb.Parent = series; series.Children.Add(lb); } + //Add episode beneath the parent + int seriesIndex = bindingList.IndexOf(series); + bindingList.Insert(seriesIndex + 1, lb); } - //Add the new product - bindingList.Insert(0, lb); + else + //Add the new product + bindingList.Insert(0, lb); } // update existing else @@ -195,24 +174,27 @@ namespace LibationWinForms var removedBooks = bindingList .AllItems() - .Where(i => i is LibraryBookEntry) - .Cast() + .LibraryBooks() .ExceptBy(dbBooks.Select(lb => lb.Book.AudibleProductId), ge => ge.AudibleProductId); + foreach (var removed in removedBooks.Where(b => b.Parent is not null)) + { + ((SeriesEntry)removed.Parent).Children.Remove(removed); + } + //Remove series that have no children var removedSeries = bindingList .AllItems() - .Where(i => i is SeriesEntry) - .Cast() - .Where(i => removedBooks.Count(r => r.Series == i.Series) == i.Children.Count); + .Series() + .Where(i => i.Children.Count == 0); foreach (var removed in removedBooks.Cast().Concat(removedSeries)) //no need to re-filter for removed books bindingList.Remove(removed); if (bindingList.Count != visibleCount) - VisibleCountChanged?.Invoke(this, bindingList.Count); + VisibleCountChanged?.Invoke(this, bindingList.LibraryBooks().Count()); } #endregion @@ -229,24 +211,19 @@ namespace LibationWinForms gridEntryBindingSource.Filter = searchString; if (visibleCount != bindingList.Count) - VisibleCountChanged?.Invoke(this, bindingList.Count); + VisibleCountChanged?.Invoke(this, bindingList.LibraryBooks().Count()); + } #endregion - internal IEnumerable GetVisible() + internal IEnumerable GetVisible() => bindingList - .Where(row => row is LibraryBookEntry) - .Cast() - .Select(row => row.LibraryBook); + .LibraryBooks(); - #region Column Customizations - - // to ensure this is only ever called once: Load instead of 'override OnVisibleChanged' private void ProductsGrid_Load(object sender, EventArgs e) { - if (this.DesignMode) - return; + gridEntryDataGridView.ColumnWidthChanged += gridEntryDataGridView_ColumnWidthChanged; contextMenuStrip1.Items.Add(new ToolStripLabel("Show / Hide Columns")); contextMenuStrip1.Items.Add(new ToolStripSeparator()); @@ -259,7 +236,7 @@ namespace LibationWinForms var cmsKiller = new ContextMenuStrip(); - foreach (DataGridViewColumn column in _dataGridView.Columns) + foreach (DataGridViewColumn column in gridEntryDataGridView.Columns) { var itemName = column.DataPropertyName; var visible = gridColumnsVisibilities.GetValueOrDefault(itemName, true); @@ -288,38 +265,19 @@ namespace LibationWinForms //We must set DisplayIndex properties in ascending order foreach (var itemName in displayIndices.OrderBy(i => i.Value).Select(i => i.Key)) { - var column = _dataGridView.Columns + var column = gridEntryDataGridView.Columns .Cast() .Single(c => c.DataPropertyName == itemName); column.DisplayIndex = displayIndices.GetValueOrDefault(itemName, column.Index); } } - - private void gridEntryDataGridView_ColumnDisplayIndexChanged(object sender, DataGridViewColumnEventArgs e) - { - var config = Configuration.Instance; - - var dictionary = config.GridColumnsDisplayIndices; - dictionary[e.Column.DataPropertyName] = e.Column.DisplayIndex; - config.GridColumnsDisplayIndices = dictionary; - } - - private void gridEntryDataGridView_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e) - { - var config = Configuration.Instance; - - var dictionary = config.GridColumnsWidths; - dictionary[e.Column.DataPropertyName] = e.Column.Width; - config.GridColumnsWidths = dictionary; - } - private void HideMenuItem_Click(object sender, EventArgs e) { var menuItem = sender as ToolStripMenuItem; var propertyName = menuItem.Tag as string; - var column = _dataGridView.Columns + var column = gridEntryDataGridView.Columns .Cast() .FirstOrDefault(c => c.DataPropertyName == propertyName); @@ -337,6 +295,15 @@ namespace LibationWinForms } } + private void gridEntryDataGridView_ColumnDisplayIndexChanged(object sender, DataGridViewColumnEventArgs e) + { + var config = Configuration.Instance; + + var dictionary = config.GridColumnsDisplayIndices; + dictionary[e.Column.DataPropertyName] = e.Column.DisplayIndex; + config.GridColumnsDisplayIndices = dictionary; + } + private void gridEntryDataGridView_CellToolTipTextNeeded(object sender, DataGridViewCellToolTipTextNeededEventArgs e) { if (e.ColumnIndex == descriptionGVColumn.Index) @@ -345,6 +312,12 @@ namespace LibationWinForms e.ToolTipText = "Click to see full size"; } - #endregion + private void gridEntryDataGridView_ColumnWidthChanged(object sender, DataGridViewColumnEventArgs e) + { + var config = Configuration.Instance; + + var dictionary = config.GridColumnsWidths; + dictionary[e.Column.DataPropertyName] = e.Column.Width; + } } } diff --git a/Source/LibationWinForms/grid/ProductsGrid.resx b/Source/LibationWinForms/grid/ProductsGrid.resx index be5db7af..2a349cc7 100644 --- a/Source/LibationWinForms/grid/ProductsGrid.resx +++ b/Source/LibationWinForms/grid/ProductsGrid.resx @@ -57,7 +57,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 81 + + 17, 17 \ No newline at end of file diff --git a/Source/LibationWinForms/grid/SeriesEntry.cs b/Source/LibationWinForms/grid/SeriesEntry.cs index 6a1e51b8..22252e7b 100644 --- a/Source/LibationWinForms/grid/SeriesEntry.cs +++ b/Source/LibationWinForms/grid/SeriesEntry.cs @@ -12,6 +12,7 @@ namespace LibationWinForms { internal class SeriesEntry : GridEntry { + public List Children { get; set; } public override DateTime DateAdded => Children.Max(c => c.DateAdded); public override string ProductRating { get; protected set; } public override string PurchaseDate { get; protected set; } @@ -24,7 +25,7 @@ namespace LibationWinForms public override string Category { get; protected set; } public override string Misc { get; protected set; } public override string Description { get; protected set; } - public override string DisplayTags => string.Empty; + public override string DisplayTags { get; } = string.Empty; public override LiberateStatus Liberate => _liberate; @@ -41,18 +42,17 @@ namespace LibationWinForms // Immutable properties { - var childLB = Children.Cast(); - int bookLenMins = childLB.Sum(c => c.LibraryBook.Book.LengthInMinutes); + int bookLenMins = Children.Sum(c => c.LibraryBook.Book.LengthInMinutes); - var myAverageRating = new Rating(childLB.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.OverallRating), childLB.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.PerformanceRating), childLB.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.StoryRating)); - var productAverageRating = new Rating(childLB.Average(c => c.LibraryBook.Book.Rating.OverallRating), childLB.Average(c => c.LibraryBook.Book.Rating.PerformanceRating), childLB.Average(c => c.LibraryBook.Book.Rating.StoryRating)); + var myAverageRating = new Rating(Children.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.OverallRating), Children.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.PerformanceRating), Children.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.StoryRating)); + var productAverageRating = new Rating(Children.Average(c => c.LibraryBook.Book.Rating.OverallRating), Children.Average(c => c.LibraryBook.Book.Rating.PerformanceRating), Children.Average(c => c.LibraryBook.Book.Rating.StoryRating)); Title = SeriesBook.Series.Name; Series = SeriesBook.Series.Name; Length = bookLenMins == 0 ? "" : $"{bookLenMins / 60} hr {bookLenMins % 60} min"; MyRating = myAverageRating.ToStarString()?.DefaultIfNullOrWhiteSpace(""); - PurchaseDate = childLB.Min(c => c.LibraryBook.DateAdded).ToString("d"); + PurchaseDate = Children.Min(c => c.LibraryBook.DateAdded).ToString("d"); ProductRating = productAverageRating.ToStarString()?.DefaultIfNullOrWhiteSpace(""); Authors = Book.AuthorNames(); Narrators = Book.NarratorNames(); @@ -73,17 +73,17 @@ namespace LibationWinForms { { nameof(Title), () => Book.SeriesSortable() }, { nameof(Series), () => Book.SeriesSortable() }, - { nameof(Length), () => Children.Cast().Sum(c=>c.LibraryBook.Book.LengthInMinutes) }, - { nameof(MyRating), () => Children.Cast().Average(c=>c.LibraryBook.Book.UserDefinedItem.Rating.FirstScore()) }, - { nameof(PurchaseDate), () => Children.Cast().Min(c=>c.LibraryBook.DateAdded) }, - { nameof(ProductRating), () => Children.Cast().Average(c => c.LibraryBook.Book.Rating.FirstScore()) }, + { nameof(Length), () => Children.Sum(c=>c.LibraryBook.Book.LengthInMinutes) }, + { nameof(MyRating), () => Children.Average(c=>c.LibraryBook.Book.UserDefinedItem.Rating.FirstScore()) }, + { nameof(PurchaseDate), () => Children.Min(c=>c.LibraryBook.DateAdded) }, + { nameof(ProductRating), () => Children.Average(c => c.LibraryBook.Book.Rating.FirstScore()) }, { nameof(Authors), () => string.Empty }, { nameof(Narrators), () => string.Empty }, { nameof(Description), () => string.Empty }, { nameof(Category), () => string.Empty }, { nameof(Misc), () => string.Empty }, { nameof(DisplayTags), () => string.Empty }, - { nameof(Liberate), () => Liberate.BookStatus }, + { nameof(Liberate), () => Liberate }, { nameof(DateAdded), () => DateAdded }, }; } diff --git a/Source/LibationWinForms/grid/SortableBindingList1.cs b/Source/LibationWinForms/grid/SortableBindingList1.cs index 7d12819f..0a0606f5 100644 --- a/Source/LibationWinForms/grid/SortableBindingList1.cs +++ b/Source/LibationWinForms/grid/SortableBindingList1.cs @@ -3,8 +3,6 @@ using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace LibationWinForms { @@ -42,22 +40,20 @@ namespace LibationWinForms { List itemsList = (List)Items; - //Array.Sort() and List.Sort() are unstable sorts. OrderBy is stable. - var sortedItems = itemsList.OrderBy((ge) => ge, Comparer).ToList(); + var sortedItems = Items.OrderBy(ge => ge, Comparer).ToList(); var children = sortedItems.Where(i => i.Parent is not null).ToList(); - var parents = sortedItems.Where(i => i.Children is not null).ToList(); - - //Top Level items - var topLevelItems = sortedItems.Except(children); itemsList.Clear(); - itemsList.AddRange(topLevelItems); - foreach (var p in parents) + //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(p); - foreach (var c in children.Where(c=> c.Parent == p)) + var pIndex = itemsList.IndexOf(parent); + foreach (var c in children.Where(c=> c.Parent == parent)) itemsList.Insert(++pIndex, c); } } @@ -86,26 +82,5 @@ namespace LibationWinForms OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); } - - protected override int FindCore(PropertyDescriptor property, object key) - { - int count = Count; - - System.Collections.IComparer valueComparer = null; - - for (int i = 0; i < count; ++i) - { - var element = this[i]; - var elemValue = element.GetMemberValue(property.Name); - valueComparer ??= element.GetMemberComparer(elemValue.GetType()); - - if (valueComparer.Compare(elemValue, key) == 0) - { - return i; - } - } - - return -1; - } } } From 440550ded91817c05d19d440bd13031d35fe8da5 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Mon, 23 May 2022 16:05:00 -0600 Subject: [PATCH 04/15] Add binding source at design time --- .../EditTagsDataGridViewImageButtonColumn.cs | 2 +- .../LiberateDataGridViewImageButtonColumn.cs | 4 +- .../grid/ProductsDisplay.Designer.cs | 1 - .../LibationWinForms/grid/ProductsDisplay.cs | 16 ++----- .../grid/ProductsGrid.Designer.cs | 37 +++++++++++----- Source/LibationWinForms/grid/ProductsGrid.cs | 26 ++++++----- .../LibationWinForms/grid/ProductsGrid.resx | 9 ++++ Source/LibationWinForms/grid/SeriesEntry.cs | 44 +++++++++++++------ 8 files changed, 87 insertions(+), 52 deletions(-) diff --git a/Source/LibationWinForms/grid/EditTagsDataGridViewImageButtonColumn.cs b/Source/LibationWinForms/grid/EditTagsDataGridViewImageButtonColumn.cs index db8bb484..b0d9407e 100644 --- a/Source/LibationWinForms/grid/EditTagsDataGridViewImageButtonColumn.cs +++ b/Source/LibationWinForms/grid/EditTagsDataGridViewImageButtonColumn.cs @@ -21,7 +21,7 @@ namespace LibationWinForms { if (rowIndex >= 0 && DataGridView.GetBoundItem(rowIndex) is SeriesEntry) { - base.Paint(graphics, clipBounds, cellBounds, rowIndex, DataGridViewElementStates.None, null, null, null, cellStyle, advancedBorderStyle, DataGridViewPaintParts.Background | DataGridViewPaintParts.Border); + base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, DataGridViewPaintParts.Background | DataGridViewPaintParts.Border); return; } diff --git a/Source/LibationWinForms/grid/LiberateDataGridViewImageButtonColumn.cs b/Source/LibationWinForms/grid/LiberateDataGridViewImageButtonColumn.cs index 159b1e4a..9d97dca9 100644 --- a/Source/LibationWinForms/grid/LiberateDataGridViewImageButtonColumn.cs +++ b/Source/LibationWinForms/grid/LiberateDataGridViewImageButtonColumn.cs @@ -25,13 +25,11 @@ namespace LibationWinForms if (status.IsSeries) { var imageName = status.Expanded ? "minus" : "plus"; - var text = status.Expanded ? "Click to Collpase" : "Click to Expand"; var bmp = (Bitmap)Properties.Resources.ResourceManager.GetObject(imageName); DrawButtonImage(graphics, bmp, cellBounds); - ToolTipText = text; - + ToolTipText = status.Expanded ? "Click to Collpase" : "Click to Expand"; } else { diff --git a/Source/LibationWinForms/grid/ProductsDisplay.Designer.cs b/Source/LibationWinForms/grid/ProductsDisplay.Designer.cs index a10e1c95..8f561b0f 100644 --- a/Source/LibationWinForms/grid/ProductsDisplay.Designer.cs +++ b/Source/LibationWinForms/grid/ProductsDisplay.Designer.cs @@ -53,7 +53,6 @@ this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.Name = "ProductsDisplay"; this.Size = new System.Drawing.Size(1510, 380); - this.Load += new System.EventHandler(this.ProductsDisplay_Load); this.ResumeLayout(false); } diff --git a/Source/LibationWinForms/grid/ProductsDisplay.cs b/Source/LibationWinForms/grid/ProductsDisplay.cs index 0a9bee61..6fc37fa0 100644 --- a/Source/LibationWinForms/grid/ProductsDisplay.cs +++ b/Source/LibationWinForms/grid/ProductsDisplay.cs @@ -14,23 +14,18 @@ namespace LibationWinForms { public partial class ProductsDisplay : UserControl { - public event EventHandler LiberateClicked; /// Number of visible rows has changed public event EventHandler VisibleCountChanged; + public event EventHandler LiberateClicked; + public event EventHandler InitialLoaded; + + private bool hasBeenDisplayed; public ProductsDisplay() { InitializeComponent(); } - private void ProductsDisplay_Load(object sender, EventArgs e) - { - if (DesignMode) - return; - - } - - #region Button controls private ImageDisplay imageDisplay; @@ -76,7 +71,6 @@ namespace LibationWinForms displayWindow.Show(this); } - private void productsGrid_DetailsClicked(LibraryBookEntry liveGridEntry) { var bookDetailsForm = new BookDetailsDialog(liveGridEntry.LibraryBook); @@ -88,8 +82,6 @@ namespace LibationWinForms #region UI display functions - private bool hasBeenDisplayed; - public event EventHandler InitialLoaded; public void Display() { // don't return early if lib size == 0. this will not update correctly if all books are removed diff --git a/Source/LibationWinForms/grid/ProductsGrid.Designer.cs b/Source/LibationWinForms/grid/ProductsGrid.Designer.cs index e818ed5b..cd312da0 100644 --- a/Source/LibationWinForms/grid/ProductsGrid.Designer.cs +++ b/Source/LibationWinForms/grid/ProductsGrid.Designer.cs @@ -29,7 +29,7 @@ private void InitializeComponent() { this.components = new System.ComponentModel.Container(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); this.gridEntryDataGridView = new System.Windows.Forms.DataGridView(); this.liberateGVColumn = new LibationWinForms.LiberateDataGridViewImageButtonColumn(); this.coverGVColumn = new System.Windows.Forms.DataGridViewImageColumn(); @@ -46,7 +46,11 @@ this.miscGVColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); 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 @@ -55,6 +59,7 @@ this.gridEntryDataGridView.AllowUserToDeleteRows = false; this.gridEntryDataGridView.AllowUserToOrderColumns = true; this.gridEntryDataGridView.AllowUserToResizeRows = false; + this.gridEntryDataGridView.AutoGenerateColumns = false; this.gridEntryDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.gridEntryDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.liberateGVColumn, @@ -72,14 +77,15 @@ this.miscGVColumn, this.tagAndDetailsGVColumn}); this.gridEntryDataGridView.ContextMenuStrip = this.contextMenuStrip1; - dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; - dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Window; - dataGridViewCellStyle1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); - dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.ControlText; - dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight; - dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText; - dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True; - this.gridEntryDataGridView.DefaultCellStyle = dataGridViewCellStyle1; + this.gridEntryDataGridView.DataSource = this.syncBindingSource; + dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Window; + dataGridViewCellStyle2.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText; + dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.gridEntryDataGridView.DefaultCellStyle = dataGridViewCellStyle2; this.gridEntryDataGridView.Dock = System.Windows.Forms.DockStyle.Fill; this.gridEntryDataGridView.Location = new System.Drawing.Point(0, 0); this.gridEntryDataGridView.Name = "gridEntryDataGridView"; @@ -90,7 +96,6 @@ this.gridEntryDataGridView.TabIndex = 0; this.gridEntryDataGridView.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.DataGridView_CellContentClick); this.gridEntryDataGridView.CellToolTipTextNeeded += new System.Windows.Forms.DataGridViewCellToolTipTextNeededEventHandler(this.gridEntryDataGridView_CellToolTipTextNeeded); - this.gridEntryDataGridView.ColumnDisplayIndexChanged += new System.Windows.Forms.DataGridViewColumnEventHandler(this.gridEntryDataGridView_ColumnDisplayIndexChanged); // // liberateGVColumn // @@ -209,6 +214,14 @@ this.contextMenuStrip1.Name = "contextMenuStrip1"; this.contextMenuStrip1.Size = new System.Drawing.Size(61, 4); // + // syncBindingSource + // + this.syncBindingSource.DataSource = this.bindingSource; + // + // bindingSource + // + this.bindingSource.DataSource = typeof(LibationWinForms.GridEntry); + // // ProductsGrid // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); @@ -219,6 +232,8 @@ this.Size = new System.Drawing.Size(1510, 380); 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); } @@ -241,5 +256,7 @@ private System.Windows.Forms.DataGridViewTextBoxColumn myRatingGVColumn; 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 b119285d..0593d743 100644 --- a/Source/LibationWinForms/grid/ProductsGrid.cs +++ b/Source/LibationWinForms/grid/ProductsGrid.cs @@ -24,15 +24,14 @@ namespace LibationWinForms.grid public new event EventHandler Scroll; private FilterableSortableBindingList bindingList; - private SyncBindingSource gridEntryBindingSource; + internal IEnumerable GetVisible() + => bindingList + .LibraryBooks(); public ProductsGrid() { InitializeComponent(); EnableDoubleBuffering(); - //There a bug in designer that causes errors if you add BindingSource to the DataGridView at design time. - gridEntryBindingSource = new SyncBindingSource(); - gridEntryDataGridView.DataSource = gridEntryBindingSource; gridEntryDataGridView.Scroll += (_, s) => Scroll?.Invoke(this, s); } @@ -105,14 +104,14 @@ namespace LibationWinForms.grid bindingList = new FilterableSortableBindingList(geList.OrderByDescending(e => e.DateAdded)); bindingList.CollapseAll(); - gridEntryBindingSource.DataSource = bindingList; + syncBindingSource.DataSource = bindingList; VisibleCountChanged?.Invoke(this, bindingList.LibraryBooks().Count()); } internal void UpdateGrid(List dbBooks) { int visibleCount = bindingList.Count; - string existingFilter = gridEntryBindingSource.Filter; + string existingFilter = syncBindingSource.Filter; //Add absent books to grid, or update current books @@ -206,9 +205,9 @@ namespace LibationWinForms.grid int visibleCount = bindingList.Count; if (string.IsNullOrEmpty(searchString)) - gridEntryBindingSource.RemoveFilter(); + syncBindingSource.RemoveFilter(); else - gridEntryBindingSource.Filter = searchString; + syncBindingSource.Filter = searchString; if (visibleCount != bindingList.Count) VisibleCountChanged?.Invoke(this, bindingList.LibraryBooks().Count()); @@ -217,13 +216,15 @@ namespace LibationWinForms.grid #endregion - internal IEnumerable GetVisible() - => bindingList - .LibraryBooks(); + #region Column Customizations private void ProductsGrid_Load(object sender, EventArgs e) { + //https://stackoverflow.com/a/4498512/3335599 + if (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime) return; + gridEntryDataGridView.ColumnWidthChanged += gridEntryDataGridView_ColumnWidthChanged; + gridEntryDataGridView.ColumnDisplayIndexChanged += gridEntryDataGridView_ColumnDisplayIndexChanged; contextMenuStrip1.Items.Add(new ToolStripLabel("Show / Hide Columns")); contextMenuStrip1.Items.Add(new ToolStripSeparator()); @@ -272,6 +273,7 @@ namespace LibationWinForms.grid column.DisplayIndex = displayIndices.GetValueOrDefault(itemName, column.Index); } } + private void HideMenuItem_Click(object sender, EventArgs e) { var menuItem = sender as ToolStripMenuItem; @@ -319,5 +321,7 @@ namespace LibationWinForms.grid var dictionary = config.GridColumnsWidths; dictionary[e.Column.DataPropertyName] = e.Column.Width; } + + #endregion } } diff --git a/Source/LibationWinForms/grid/ProductsGrid.resx b/Source/LibationWinForms/grid/ProductsGrid.resx index 2a349cc7..bc15cd01 100644 --- a/Source/LibationWinForms/grid/ProductsGrid.resx +++ b/Source/LibationWinForms/grid/ProductsGrid.resx @@ -58,6 +58,15 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + 171, 17 + + 17, 17 + + 326, 17 + + + 326, 17 + \ No newline at end of file diff --git a/Source/LibationWinForms/grid/SeriesEntry.cs b/Source/LibationWinForms/grid/SeriesEntry.cs index 22252e7b..afe21444 100644 --- a/Source/LibationWinForms/grid/SeriesEntry.cs +++ b/Source/LibationWinForms/grid/SeriesEntry.cs @@ -14,17 +14,41 @@ namespace LibationWinForms { public List Children { get; set; } public override DateTime DateAdded => Children.Max(c => c.DateAdded); - public override string ProductRating { get; protected set; } + public override string ProductRating + { + get + { + var productAverageRating = new Rating(Children.Average(c => c.LibraryBook.Book.Rating.OverallRating), Children.Average(c => c.LibraryBook.Book.Rating.PerformanceRating), Children.Average(c => c.LibraryBook.Book.Rating.StoryRating)); + return productAverageRating.ToStarString()?.DefaultIfNullOrWhiteSpace(""); + } + protected set => throw new NotImplementedException(); + } public override string PurchaseDate { get; protected set; } - public override string MyRating { get; protected set; } + public override string MyRating + { + get + { + var myAverageRating = new Rating(Children.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.OverallRating), Children.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.PerformanceRating), Children.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.StoryRating)); + return myAverageRating.ToStarString()?.DefaultIfNullOrWhiteSpace(""); + } + protected set => throw new NotImplementedException(); + } public override string Series { get; protected set; } public override string Title { get; protected set; } - public override string Length { get; protected set; } + public override string Length + { + get + { + int bookLenMins = Children.Sum(c => c.LibraryBook.Book.LengthInMinutes); + return bookLenMins == 0 ? "" : $"{bookLenMins / 60} hr {bookLenMins % 60} min"; + } + protected set => throw new NotImplementedException(); + } public override string Authors { get; protected set; } public override string Narrators { get; protected set; } public override string Category { get; protected set; } - public override string Misc { get; protected set; } - public override string Description { get; protected set; } + public override string Misc { get; protected set; } = string.Empty; + public override string Description { get; protected set; } = string.Empty; public override string DisplayTags { get; } = string.Empty; public override LiberateStatus Liberate => _liberate; @@ -34,6 +58,7 @@ namespace LibationWinForms private SeriesBook SeriesBook { get; set; } private LiberateStatus _liberate = new LiberateStatus { IsSeries = true }; + public void setSeriesBook(SeriesBook seriesBook) { SeriesBook = seriesBook; @@ -42,18 +67,9 @@ namespace LibationWinForms // Immutable properties { - int bookLenMins = Children.Sum(c => c.LibraryBook.Book.LengthInMinutes); - - var myAverageRating = new Rating(Children.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.OverallRating), Children.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.PerformanceRating), Children.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.StoryRating)); - var productAverageRating = new Rating(Children.Average(c => c.LibraryBook.Book.Rating.OverallRating), Children.Average(c => c.LibraryBook.Book.Rating.PerformanceRating), Children.Average(c => c.LibraryBook.Book.Rating.StoryRating)); - - Title = SeriesBook.Series.Name; Series = SeriesBook.Series.Name; - Length = bookLenMins == 0 ? "" : $"{bookLenMins / 60} hr {bookLenMins % 60} min"; - MyRating = myAverageRating.ToStarString()?.DefaultIfNullOrWhiteSpace(""); PurchaseDate = Children.Min(c => c.LibraryBook.DateAdded).ToString("d"); - ProductRating = productAverageRating.ToStarString()?.DefaultIfNullOrWhiteSpace(""); Authors = Book.AuthorNames(); Narrators = Book.NarratorNames(); Category = string.Join(" > ", Book.CategoriesNames()); From 718d21f6cb7dabff7726a4bc7078909877b5c0e6 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Mon, 23 May 2022 16:42:05 -0600 Subject: [PATCH 05/15] NotifyPropertyChanged series on update --- Source/LibationWinForms/grid/GridEntry.cs | 8 ++++---- .../grid/LiberateDataGridViewImageButtonColumn.cs | 2 +- Source/LibationWinForms/grid/LibraryBookEntry.cs | 4 ++-- Source/LibationWinForms/grid/ProductsGrid.cs | 6 +++++- Source/LibationWinForms/grid/SeriesEntry.cs | 8 ++------ 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/Source/LibationWinForms/grid/GridEntry.cs b/Source/LibationWinForms/grid/GridEntry.cs index e0f19c1c..5c53f7e9 100644 --- a/Source/LibationWinForms/grid/GridEntry.cs +++ b/Source/LibationWinForms/grid/GridEntry.cs @@ -15,7 +15,7 @@ namespace LibationWinForms T Parent { get; } } - public class LiberateStatus : IComparable + public class LiberateButtonStatus : IComparable { public LiberatedStatus BookStatus; public LiberatedStatus? PdfStatus; @@ -24,7 +24,7 @@ namespace LibationWinForms public int CompareTo(object obj) { - if (obj is not LiberateStatus second) return -1; + if (obj is not LiberateButtonStatus second) return -1; if (IsSeries && !second.IsSeries) return -1; else if (!IsSeries && second.IsSeries) return 1; @@ -69,7 +69,7 @@ namespace LibationWinForms public abstract string Misc { get; protected set; } public abstract string Description { get; protected set; } public abstract string DisplayTags { get; } - public abstract LiberateStatus Liberate { get; } + public abstract LiberateButtonStatus Liberate { get; } public abstract object GetMemberValue(string memberName); #endregion public IComparer GetMemberComparer(Type memberType) => _memberTypeComparers[memberType]; @@ -105,7 +105,7 @@ namespace LibationWinForms { typeof(float), new ObjectComparer() }, { typeof(bool), new ObjectComparer() }, { typeof(DateTime), new ObjectComparer() }, - { typeof(LiberateStatus), new ObjectComparer() }, + { typeof(LiberateButtonStatus), new ObjectComparer() }, }; ~GridEntry() diff --git a/Source/LibationWinForms/grid/LiberateDataGridViewImageButtonColumn.cs b/Source/LibationWinForms/grid/LiberateDataGridViewImageButtonColumn.cs index 9d97dca9..feb9b0e0 100644 --- a/Source/LibationWinForms/grid/LiberateDataGridViewImageButtonColumn.cs +++ b/Source/LibationWinForms/grid/LiberateDataGridViewImageButtonColumn.cs @@ -20,7 +20,7 @@ namespace LibationWinForms { base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, paintParts); - if (value is LiberateStatus status) + if (value is LiberateButtonStatus status) { if (status.IsSeries) { diff --git a/Source/LibationWinForms/grid/LibraryBookEntry.cs b/Source/LibationWinForms/grid/LibraryBookEntry.cs index c1be718c..a02686eb 100644 --- a/Source/LibationWinForms/grid/LibraryBookEntry.cs +++ b/Source/LibationWinForms/grid/LibraryBookEntry.cs @@ -53,7 +53,7 @@ namespace LibationWinForms public override string DisplayTags => string.Join("\r\n", Book.UserDefinedItem.TagsEnumerated); // these 2 values being in 1 field is the trick behind getting the liberated+pdf 'stoplight' icon to draw. See: LiberateDataGridViewImageButtonCell.Paint - public override LiberateStatus Liberate + public override LiberateButtonStatus Liberate { get { @@ -64,7 +64,7 @@ namespace LibationWinForms _pdfStatus = LibraryCommands.Pdf_Status(LibraryBook.Book); lastStatusUpdate = DateTime.Now; } - return new LiberateStatus { BookStatus = _bookStatus, PdfStatus = _pdfStatus, IsSeries = false }; + return new LiberateButtonStatus { BookStatus = _bookStatus, PdfStatus = _pdfStatus, IsSeries = false }; } } #endregion diff --git a/Source/LibationWinForms/grid/ProductsGrid.cs b/Source/LibationWinForms/grid/ProductsGrid.cs index 0593d743..f42a6de1 100644 --- a/Source/LibationWinForms/grid/ProductsGrid.cs +++ b/Source/LibationWinForms/grid/ProductsGrid.cs @@ -149,6 +149,8 @@ namespace LibationWinForms.grid //Add episode beneath the parent int seriesIndex = bindingList.IndexOf(series); bindingList.Insert(seriesIndex + 1, lb); + + series.NotifyPropertyChanged(); } else //Add the new product @@ -178,7 +180,9 @@ namespace LibationWinForms.grid foreach (var removed in removedBooks.Where(b => b.Parent is not null)) { - ((SeriesEntry)removed.Parent).Children.Remove(removed); + var series = removed.Parent as SeriesEntry; + series.Children.Remove(removed); + series.NotifyPropertyChanged(); } //Remove series that have no children diff --git a/Source/LibationWinForms/grid/SeriesEntry.cs b/Source/LibationWinForms/grid/SeriesEntry.cs index afe21444..bc74f147 100644 --- a/Source/LibationWinForms/grid/SeriesEntry.cs +++ b/Source/LibationWinForms/grid/SeriesEntry.cs @@ -1,12 +1,8 @@ using DataLayer; using Dinah.Core; using System; -using System.Collections; using System.Collections.Generic; -using System.Drawing; using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace LibationWinForms { @@ -51,13 +47,13 @@ namespace LibationWinForms public override string Description { get; protected set; } = string.Empty; public override string DisplayTags { get; } = string.Empty; - public override LiberateStatus Liberate => _liberate; + public override LiberateButtonStatus Liberate => _liberate; protected override Book Book => SeriesBook.Book; private SeriesBook SeriesBook { get; set; } - private LiberateStatus _liberate = new LiberateStatus { IsSeries = true }; + private LiberateButtonStatus _liberate = new LiberateButtonStatus { IsSeries = true }; public void setSeriesBook(SeriesBook seriesBook) { From f29dff33868c2ebd9c9c72460ed83b954247023f Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Mon, 23 May 2022 17:22:02 -0600 Subject: [PATCH 06/15] Fix filtering bug --- Source/LibationWinForms/grid/ProductsGrid.cs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Source/LibationWinForms/grid/ProductsGrid.cs b/Source/LibationWinForms/grid/ProductsGrid.cs index f42a6de1..f0fc1497 100644 --- a/Source/LibationWinForms/grid/ProductsGrid.cs +++ b/Source/LibationWinForms/grid/ProductsGrid.cs @@ -110,8 +110,8 @@ namespace LibationWinForms.grid internal void UpdateGrid(List dbBooks) { - int visibleCount = bindingList.Count; string existingFilter = syncBindingSource.Filter; + Filter(null); //Add absent books to grid, or update current books @@ -150,6 +150,11 @@ namespace LibationWinForms.grid int seriesIndex = bindingList.IndexOf(series); bindingList.Insert(seriesIndex + 1, lb); + if (series.Liberate.Expanded) + bindingList.ExpandItem(series); + else + bindingList.CollapseItem(series); + series.NotifyPropertyChanged(); } else @@ -163,13 +168,6 @@ namespace LibationWinForms.grid } } - if (bindingList.Count != visibleCount) - { - //re-filter for newly added items - Filter(null); - 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 = @@ -196,8 +194,9 @@ namespace LibationWinForms.grid //no need to re-filter for removed books bindingList.Remove(removed); - if (bindingList.Count != visibleCount) - VisibleCountChanged?.Invoke(this, bindingList.LibraryBooks().Count()); + Filter(existingFilter); + + VisibleCountChanged?.Invoke(this, bindingList.LibraryBooks().Count()); } #endregion From 4a82541ffdaa9ffa95e91a93a0b52da2f6ef3296 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Mon, 23 May 2022 17:46:55 -0600 Subject: [PATCH 07/15] Fix error while removing filter on a sorted binding list --- Source/LibationWinForms/grid/FilterableSortableBindingList.cs | 4 ++++ Source/LibationWinForms/grid/SortableBindingList1.cs | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Source/LibationWinForms/grid/FilterableSortableBindingList.cs b/Source/LibationWinForms/grid/FilterableSortableBindingList.cs index e309f7fe..dcb70524 100644 --- a/Source/LibationWinForms/grid/FilterableSortableBindingList.cs +++ b/Source/LibationWinForms/grid/FilterableSortableBindingList.cs @@ -120,6 +120,8 @@ namespace LibationWinForms int visibleCount = Items.Count; + SuspendSorting = true; + foreach (var item in FilterRemoved.ToList()) { if (item.Parent is null || item.Parent.Liberate.Expanded) @@ -129,6 +131,8 @@ namespace LibationWinForms } } + SuspendSorting = false; + if (IsSortedCore) Sort(); else diff --git a/Source/LibationWinForms/grid/SortableBindingList1.cs b/Source/LibationWinForms/grid/SortableBindingList1.cs index 0a0606f5..ec545340 100644 --- a/Source/LibationWinForms/grid/SortableBindingList1.cs +++ b/Source/LibationWinForms/grid/SortableBindingList1.cs @@ -15,6 +15,7 @@ namespace LibationWinForms 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; @@ -60,7 +61,7 @@ namespace LibationWinForms protected override void OnListChanged(ListChangedEventArgs e) { - if (isSorted && + if (isSorted && !SuspendSorting && ((e.ListChangedType == ListChangedType.ItemChanged && e.PropertyDescriptor == SortPropertyCore) || e.ListChangedType == ListChangedType.ItemAdded)) { From d71cdecd35a45756112b88da91f1e271ed2a6a7d Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Mon, 23 May 2022 21:20:26 -0600 Subject: [PATCH 08/15] Refactoring and addressing comments --- Source/AppScaffolding/AppScaffolding.csproj | 2 +- Source/LibationWinForms/Form1.Designer.cs | 26 ++--- Source/LibationWinForms/Form1.resx | 36 +++++++ .../ProcessQueue/ProcessQueueControl.cs | 13 ++- .../ProcessQueue/VirtualFlowControl.cs | 3 - Source/LibationWinForms/grid/GridEntry.cs | 41 ++----- ...BindingList.cs => GridEntryBindingList.cs} | 101 +++++++++++++++--- .../grid/LiberateButtonStatus.cs | 28 +++++ .../LibationWinForms/grid/LibraryBookEntry.cs | 9 +- .../grid/ProductsGrid.Designer.cs | 10 +- Source/LibationWinForms/grid/ProductsGrid.cs | 50 ++++----- Source/LibationWinForms/grid/SeriesEntry.cs | 17 ++- .../grid/SortableBindingList1.cs | 87 --------------- 13 files changed, 218 insertions(+), 205 deletions(-) rename Source/LibationWinForms/grid/{FilterableSortableBindingList.cs => GridEntryBindingList.cs} (56%) create mode 100644 Source/LibationWinForms/grid/LiberateButtonStatus.cs delete mode 100644 Source/LibationWinForms/grid/SortableBindingList1.cs 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)); - } - } -} From e778c7a59d9692dcc308b2eba59588eff8c256e6 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Mon, 23 May 2022 21:34:43 -0600 Subject: [PATCH 09/15] Create GridView namespace --- .../Dialogs/RemoveBooksDialog.Designer.cs | 4 +- .../Dialogs/RemoveBooksDialog.cs | 3 +- Source/LibationWinForms/Form1.Designer.cs | 4 +- .../AsyncNotifyPropertyChanged.cs | 2 +- .../DataGridViewImageButtonCell.cs | 2 +- .../DescriptionDisplay.Designer.cs | 2 +- .../{grid => GridView}/DescriptionDisplay.cs | 2 +- .../DescriptionDisplay.resx | 0 .../EditTagsDataGridViewImageButtonColumn.cs | 2 +- .../{grid => GridView}/GridEntry.cs | 2 +- .../GridEntryBindingList.cs | 2 +- .../ImageDisplay.Designer.cs | 2 +- .../{grid => GridView}/ImageDisplay.cs | 2 +- .../{grid => GridView}/ImageDisplay.resx | 0 .../LiberateButtonStatus.cs | 2 +- .../LiberateDataGridViewImageButtonColumn.cs | 2 +- .../{grid => GridView}/LibraryBookEntry.cs | 2 +- .../ProductsDisplay.Designer.cs | 14 +++---- .../{grid => GridView}/ProductsDisplay.cs | 2 +- .../{grid => GridView}/ProductsDisplay.resx | 0 .../ProductsGrid.Designer.cs | 38 +++++++++---------- .../{grid => GridView}/ProductsGrid.cs | 2 +- .../{grid => GridView}/ProductsGrid.resx | 0 .../{grid => GridView}/SeriesEntry.cs | 2 +- .../{ => GridView}/SyncBindingSource.cs | 2 +- .../LibationWinForm.GridEntry.datasource | 2 +- 26 files changed, 48 insertions(+), 49 deletions(-) rename Source/LibationWinForms/{grid => GridView}/AsyncNotifyPropertyChanged.cs (94%) rename Source/LibationWinForms/{grid => GridView}/DataGridViewImageButtonCell.cs (92%) rename Source/LibationWinForms/{grid => GridView}/DescriptionDisplay.Designer.cs (98%) rename Source/LibationWinForms/{grid => GridView}/DescriptionDisplay.cs (97%) rename Source/LibationWinForms/{grid => GridView}/DescriptionDisplay.resx (100%) rename Source/LibationWinForms/{grid => GridView}/EditTagsDataGridViewImageButtonColumn.cs (98%) rename Source/LibationWinForms/{grid => GridView}/GridEntry.cs (99%) rename Source/LibationWinForms/{grid => GridView}/GridEntryBindingList.cs (99%) rename Source/LibationWinForms/{grid => GridView}/ImageDisplay.Designer.cs (98%) rename Source/LibationWinForms/{grid => GridView}/ImageDisplay.cs (98%) rename Source/LibationWinForms/{grid => GridView}/ImageDisplay.resx (100%) rename Source/LibationWinForms/{grid => GridView}/LiberateButtonStatus.cs (96%) rename Source/LibationWinForms/{grid => GridView}/LiberateDataGridViewImageButtonColumn.cs (98%) rename Source/LibationWinForms/{grid => GridView}/LibraryBookEntry.cs (99%) rename Source/LibationWinForms/{grid => GridView}/ProductsDisplay.Designer.cs (70%) rename Source/LibationWinForms/{grid => GridView}/ProductsDisplay.cs (99%) rename Source/LibationWinForms/{grid => GridView}/ProductsDisplay.resx (100%) rename Source/LibationWinForms/{grid => GridView}/ProductsGrid.Designer.cs (93%) rename Source/LibationWinForms/{grid => GridView}/ProductsGrid.cs (99%) rename Source/LibationWinForms/{grid => GridView}/ProductsGrid.resx (100%) rename Source/LibationWinForms/{grid => GridView}/SeriesEntry.cs (99%) rename Source/LibationWinForms/{ => GridView}/SyncBindingSource.cs (96%) diff --git a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.Designer.cs b/Source/LibationWinForms/Dialogs/RemoveBooksDialog.Designer.cs index 61974264..7240b8f3 100644 --- a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.Designer.cs +++ b/Source/LibationWinForms/Dialogs/RemoveBooksDialog.Designer.cs @@ -38,7 +38,7 @@ namespace LibationWinForms.Dialogs this.authorsDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.miscDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.purchaseDateGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.gridEntryBindingSource = new LibationWinForms.SyncBindingSource(this.components); + this.gridEntryBindingSource = new LibationWinForms.GridView.SyncBindingSource(this.components); this.btnRemoveBooks = new System.Windows.Forms.Button(); this.label1 = new System.Windows.Forms.Label(); ((System.ComponentModel.ISupportInitialize)(this._dataGridView)).BeginInit(); @@ -176,7 +176,7 @@ namespace LibationWinForms.Dialogs #endregion private System.Windows.Forms.DataGridView _dataGridView; - private LibationWinForms.SyncBindingSource gridEntryBindingSource; + private LibationWinForms.GridView.SyncBindingSource gridEntryBindingSource; private System.Windows.Forms.Button btnRemoveBooks; private System.Windows.Forms.Label label1; private System.Windows.Forms.DataGridViewCheckBoxColumn removeDataGridViewCheckBoxColumn; diff --git a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs b/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs index bb1759dc..558095f0 100644 --- a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs +++ b/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Data; @@ -121,7 +120,7 @@ namespace LibationWinForms.Dialogs } } - internal class RemovableGridEntry : LibraryBookEntry + internal class RemovableGridEntry : GridView.LibraryBookEntry { private bool _remove = false; public RemovableGridEntry(LibraryBook libraryBook) : base(libraryBook) { } diff --git a/Source/LibationWinForms/Form1.Designer.cs b/Source/LibationWinForms/Form1.Designer.cs index 49c41b42..f4abad4f 100644 --- a/Source/LibationWinForms/Form1.Designer.cs +++ b/Source/LibationWinForms/Form1.Designer.cs @@ -72,7 +72,7 @@ this.addQuickFilterBtn = new System.Windows.Forms.Button(); this.splitContainer1 = new System.Windows.Forms.SplitContainer(); this.panel1 = new System.Windows.Forms.Panel(); - this.productsDisplay = new LibationWinForms.ProductsDisplay(); + this.productsDisplay = new LibationWinForms.GridView.ProductsDisplay(); this.toggleQueueHideBtn = new System.Windows.Forms.Button(); this.processBookQueue1 = new LibationWinForms.ProcessQueue.ProcessQueueControl(); this.menuStrip1.SuspendLayout(); @@ -583,6 +583,6 @@ private LibationWinForms.ProcessQueue.ProcessQueueControl processBookQueue1; private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Button toggleQueueHideBtn; - private ProductsDisplay productsDisplay; + private LibationWinForms.GridView.ProductsDisplay productsDisplay; } } diff --git a/Source/LibationWinForms/grid/AsyncNotifyPropertyChanged.cs b/Source/LibationWinForms/GridView/AsyncNotifyPropertyChanged.cs similarity index 94% rename from Source/LibationWinForms/grid/AsyncNotifyPropertyChanged.cs rename to Source/LibationWinForms/GridView/AsyncNotifyPropertyChanged.cs index cce5e5b9..e4532cfe 100644 --- a/Source/LibationWinForms/grid/AsyncNotifyPropertyChanged.cs +++ b/Source/LibationWinForms/GridView/AsyncNotifyPropertyChanged.cs @@ -2,7 +2,7 @@ using System.ComponentModel; using System.Runtime.CompilerServices; -namespace LibationWinForms +namespace LibationWinForms.GridView { public abstract class AsyncNotifyPropertyChanged : SynchronizeInvoker, INotifyPropertyChanged { diff --git a/Source/LibationWinForms/grid/DataGridViewImageButtonCell.cs b/Source/LibationWinForms/GridView/DataGridViewImageButtonCell.cs similarity index 92% rename from Source/LibationWinForms/grid/DataGridViewImageButtonCell.cs rename to Source/LibationWinForms/GridView/DataGridViewImageButtonCell.cs index f4f25afd..d53b02ed 100644 --- a/Source/LibationWinForms/grid/DataGridViewImageButtonCell.cs +++ b/Source/LibationWinForms/GridView/DataGridViewImageButtonCell.cs @@ -1,7 +1,7 @@ using System.Drawing; using System.Windows.Forms; -namespace LibationWinForms +namespace LibationWinForms.GridView { public class DataGridViewImageButtonCell : DataGridViewButtonCell { diff --git a/Source/LibationWinForms/grid/DescriptionDisplay.Designer.cs b/Source/LibationWinForms/GridView/DescriptionDisplay.Designer.cs similarity index 98% rename from Source/LibationWinForms/grid/DescriptionDisplay.Designer.cs rename to Source/LibationWinForms/GridView/DescriptionDisplay.Designer.cs index 96012e50..be8d9496 100644 --- a/Source/LibationWinForms/grid/DescriptionDisplay.Designer.cs +++ b/Source/LibationWinForms/GridView/DescriptionDisplay.Designer.cs @@ -1,4 +1,4 @@ -namespace LibationWinForms +namespace LibationWinForms.GridView { partial class DescriptionDisplay { diff --git a/Source/LibationWinForms/grid/DescriptionDisplay.cs b/Source/LibationWinForms/GridView/DescriptionDisplay.cs similarity index 97% rename from Source/LibationWinForms/grid/DescriptionDisplay.cs rename to Source/LibationWinForms/GridView/DescriptionDisplay.cs index 3f95a92e..a39790d9 100644 --- a/Source/LibationWinForms/grid/DescriptionDisplay.cs +++ b/Source/LibationWinForms/GridView/DescriptionDisplay.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Runtime.InteropServices; using System.Windows.Forms; -namespace LibationWinForms +namespace LibationWinForms.GridView { public partial class DescriptionDisplay : Form { diff --git a/Source/LibationWinForms/grid/DescriptionDisplay.resx b/Source/LibationWinForms/GridView/DescriptionDisplay.resx similarity index 100% rename from Source/LibationWinForms/grid/DescriptionDisplay.resx rename to Source/LibationWinForms/GridView/DescriptionDisplay.resx diff --git a/Source/LibationWinForms/grid/EditTagsDataGridViewImageButtonColumn.cs b/Source/LibationWinForms/GridView/EditTagsDataGridViewImageButtonColumn.cs similarity index 98% rename from Source/LibationWinForms/grid/EditTagsDataGridViewImageButtonColumn.cs rename to Source/LibationWinForms/GridView/EditTagsDataGridViewImageButtonColumn.cs index b0d9407e..065afdbb 100644 --- a/Source/LibationWinForms/grid/EditTagsDataGridViewImageButtonColumn.cs +++ b/Source/LibationWinForms/GridView/EditTagsDataGridViewImageButtonColumn.cs @@ -2,7 +2,7 @@ using System.Drawing; using System.Windows.Forms; -namespace LibationWinForms +namespace LibationWinForms.GridView { public class EditTagsDataGridViewImageButtonColumn : DataGridViewButtonColumn { diff --git a/Source/LibationWinForms/grid/GridEntry.cs b/Source/LibationWinForms/GridView/GridEntry.cs similarity index 99% rename from Source/LibationWinForms/grid/GridEntry.cs rename to Source/LibationWinForms/GridView/GridEntry.cs index d26ae575..733bcac8 100644 --- a/Source/LibationWinForms/grid/GridEntry.cs +++ b/Source/LibationWinForms/GridView/GridEntry.cs @@ -8,7 +8,7 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; -namespace LibationWinForms +namespace LibationWinForms.GridView { public abstract class GridEntry : AsyncNotifyPropertyChanged, IMemberComparable { diff --git a/Source/LibationWinForms/grid/GridEntryBindingList.cs b/Source/LibationWinForms/GridView/GridEntryBindingList.cs similarity index 99% rename from Source/LibationWinForms/grid/GridEntryBindingList.cs rename to Source/LibationWinForms/GridView/GridEntryBindingList.cs index fb6f7df7..511e2ce6 100644 --- a/Source/LibationWinForms/grid/GridEntryBindingList.cs +++ b/Source/LibationWinForms/GridView/GridEntryBindingList.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; -namespace LibationWinForms +namespace LibationWinForms.GridView { /* * Allows filtering and sorting of the underlying BindingList diff --git a/Source/LibationWinForms/grid/ImageDisplay.Designer.cs b/Source/LibationWinForms/GridView/ImageDisplay.Designer.cs similarity index 98% rename from Source/LibationWinForms/grid/ImageDisplay.Designer.cs rename to Source/LibationWinForms/GridView/ImageDisplay.Designer.cs index ec1dbdf0..a1ca5caa 100644 --- a/Source/LibationWinForms/grid/ImageDisplay.Designer.cs +++ b/Source/LibationWinForms/GridView/ImageDisplay.Designer.cs @@ -1,4 +1,4 @@ -namespace LibationWinForms +namespace LibationWinForms.GridView { partial class ImageDisplay { diff --git a/Source/LibationWinForms/grid/ImageDisplay.cs b/Source/LibationWinForms/GridView/ImageDisplay.cs similarity index 98% rename from Source/LibationWinForms/grid/ImageDisplay.cs rename to Source/LibationWinForms/GridView/ImageDisplay.cs index 53f75987..a6bfa574 100644 --- a/Source/LibationWinForms/grid/ImageDisplay.cs +++ b/Source/LibationWinForms/GridView/ImageDisplay.cs @@ -5,7 +5,7 @@ using System.Drawing; using System.IO; using System.Windows.Forms; -namespace LibationWinForms +namespace LibationWinForms.GridView { public partial class ImageDisplay : Form { diff --git a/Source/LibationWinForms/grid/ImageDisplay.resx b/Source/LibationWinForms/GridView/ImageDisplay.resx similarity index 100% rename from Source/LibationWinForms/grid/ImageDisplay.resx rename to Source/LibationWinForms/GridView/ImageDisplay.resx diff --git a/Source/LibationWinForms/grid/LiberateButtonStatus.cs b/Source/LibationWinForms/GridView/LiberateButtonStatus.cs similarity index 96% rename from Source/LibationWinForms/grid/LiberateButtonStatus.cs rename to Source/LibationWinForms/GridView/LiberateButtonStatus.cs index 8814eb24..3ee577fd 100644 --- a/Source/LibationWinForms/grid/LiberateButtonStatus.cs +++ b/Source/LibationWinForms/GridView/LiberateButtonStatus.cs @@ -1,7 +1,7 @@ using DataLayer; using System; -namespace LibationWinForms +namespace LibationWinForms.GridView { public class LiberateButtonStatus : IComparable { diff --git a/Source/LibationWinForms/grid/LiberateDataGridViewImageButtonColumn.cs b/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs similarity index 98% rename from Source/LibationWinForms/grid/LiberateDataGridViewImageButtonColumn.cs rename to Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs index feb9b0e0..a237b7a0 100644 --- a/Source/LibationWinForms/grid/LiberateDataGridViewImageButtonColumn.cs +++ b/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs @@ -4,7 +4,7 @@ using System.Windows.Forms; using System.Linq; using DataLayer; -namespace LibationWinForms +namespace LibationWinForms.GridView { public class LiberateDataGridViewImageButtonColumn : DataGridViewButtonColumn { diff --git a/Source/LibationWinForms/grid/LibraryBookEntry.cs b/Source/LibationWinForms/GridView/LibraryBookEntry.cs similarity index 99% rename from Source/LibationWinForms/grid/LibraryBookEntry.cs rename to Source/LibationWinForms/GridView/LibraryBookEntry.cs index 3834e502..5fc606a1 100644 --- a/Source/LibationWinForms/grid/LibraryBookEntry.cs +++ b/Source/LibationWinForms/GridView/LibraryBookEntry.cs @@ -6,7 +6,7 @@ using ApplicationServices; using DataLayer; using Dinah.Core; -namespace LibationWinForms +namespace LibationWinForms.GridView { /// /// The View Model for a LibraryBook diff --git a/Source/LibationWinForms/grid/ProductsDisplay.Designer.cs b/Source/LibationWinForms/GridView/ProductsDisplay.Designer.cs similarity index 70% rename from Source/LibationWinForms/grid/ProductsDisplay.Designer.cs rename to Source/LibationWinForms/GridView/ProductsDisplay.Designer.cs index 8f561b0f..c4b1a543 100644 --- a/Source/LibationWinForms/grid/ProductsDisplay.Designer.cs +++ b/Source/LibationWinForms/GridView/ProductsDisplay.Designer.cs @@ -1,4 +1,4 @@ -namespace LibationWinForms +namespace LibationWinForms.GridView { partial class ProductsDisplay { @@ -28,7 +28,7 @@ /// private void InitializeComponent() { - this.productsGrid = new LibationWinForms.grid.ProductsGrid(); + this.productsGrid = new LibationWinForms.GridView.ProductsGrid(); this.SuspendLayout(); // // productsGrid @@ -39,10 +39,10 @@ this.productsGrid.Name = "productsGrid"; this.productsGrid.Size = new System.Drawing.Size(1510, 380); this.productsGrid.TabIndex = 0; - this.productsGrid.LiberateClicked += new LibationWinForms.grid.ProductsGrid.LibraryBookEntryClickedEventHandler(this.productsGrid_LiberateClicked); - this.productsGrid.CoverClicked += new LibationWinForms.grid.ProductsGrid.LibraryBookEntryClickedEventHandler(this.productsGrid_CoverClicked); - this.productsGrid.DetailsClicked += new LibationWinForms.grid.ProductsGrid.LibraryBookEntryClickedEventHandler(this.productsGrid_DetailsClicked); - this.productsGrid.DescriptionClicked += new LibationWinForms.grid.ProductsGrid.LibraryBookEntryRectangleClickedEventHandler(this.productsGrid_DescriptionClicked); + this.productsGrid.LiberateClicked += new LibationWinForms.GridView.ProductsGrid.LibraryBookEntryClickedEventHandler(this.productsGrid_LiberateClicked); + this.productsGrid.CoverClicked += new LibationWinForms.GridView.ProductsGrid.LibraryBookEntryClickedEventHandler(this.productsGrid_CoverClicked); + this.productsGrid.DetailsClicked += new LibationWinForms.GridView.ProductsGrid.LibraryBookEntryClickedEventHandler(this.productsGrid_DetailsClicked); + this.productsGrid.DescriptionClicked += new LibationWinForms.GridView.ProductsGrid.LibraryBookEntryRectangleClickedEventHandler(this.productsGrid_DescriptionClicked); this.productsGrid.VisibleCountChanged += new System.EventHandler(this.productsGrid_VisibleCountChanged); // // ProductsDisplay @@ -59,6 +59,6 @@ #endregion - private grid.ProductsGrid productsGrid; + private GridView.ProductsGrid productsGrid; } } diff --git a/Source/LibationWinForms/grid/ProductsDisplay.cs b/Source/LibationWinForms/GridView/ProductsDisplay.cs similarity index 99% rename from Source/LibationWinForms/grid/ProductsDisplay.cs rename to Source/LibationWinForms/GridView/ProductsDisplay.cs index 6fc37fa0..66de694b 100644 --- a/Source/LibationWinForms/grid/ProductsDisplay.cs +++ b/Source/LibationWinForms/GridView/ProductsDisplay.cs @@ -10,7 +10,7 @@ using FileLiberator; using LibationFileManager; using LibationWinForms.Dialogs; -namespace LibationWinForms +namespace LibationWinForms.GridView { public partial class ProductsDisplay : UserControl { diff --git a/Source/LibationWinForms/grid/ProductsDisplay.resx b/Source/LibationWinForms/GridView/ProductsDisplay.resx similarity index 100% rename from Source/LibationWinForms/grid/ProductsDisplay.resx rename to Source/LibationWinForms/GridView/ProductsDisplay.resx diff --git a/Source/LibationWinForms/grid/ProductsGrid.Designer.cs b/Source/LibationWinForms/GridView/ProductsGrid.Designer.cs similarity index 93% rename from Source/LibationWinForms/grid/ProductsGrid.Designer.cs rename to Source/LibationWinForms/GridView/ProductsGrid.Designer.cs index fec72094..b380ff95 100644 --- a/Source/LibationWinForms/grid/ProductsGrid.Designer.cs +++ b/Source/LibationWinForms/GridView/ProductsGrid.Designer.cs @@ -1,4 +1,4 @@ -namespace LibationWinForms.grid +namespace LibationWinForms.GridView { partial class ProductsGrid { @@ -31,7 +31,7 @@ this.components = new System.ComponentModel.Container(); System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); this.gridEntryDataGridView = new System.Windows.Forms.DataGridView(); - this.liberateGVColumn = new LibationWinForms.LiberateDataGridViewImageButtonColumn(); + this.liberateGVColumn = new LibationWinForms.GridView.LiberateDataGridViewImageButtonColumn(); this.coverGVColumn = new System.Windows.Forms.DataGridViewImageColumn(); this.titleGVColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.authorsGVColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); @@ -44,9 +44,9 @@ this.purchaseDateGVColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.myRatingGVColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.miscGVColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.tagAndDetailsGVColumn = new LibationWinForms.EditTagsDataGridViewImageButtonColumn(); + this.tagAndDetailsGVColumn = new LibationWinForms.GridView.EditTagsDataGridViewImageButtonColumn(); this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); - this.syncBindingSource = new LibationWinForms.SyncBindingSource(this.components); + this.syncBindingSource = new LibationWinForms.GridView.SyncBindingSource(this.components); ((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).BeginInit(); ((System.ComponentModel.ISupportInitialize)(this.syncBindingSource)).BeginInit(); this.SuspendLayout(); @@ -60,20 +60,20 @@ this.gridEntryDataGridView.AutoGenerateColumns = false; this.gridEntryDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.gridEntryDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { - this.liberateGVColumn, - this.coverGVColumn, - this.titleGVColumn, - this.authorsGVColumn, - this.narratorsGVColumn, - this.lengthGVColumn, - this.seriesGVColumn, - this.descriptionGVColumn, - this.categoryGVColumn, - this.productRatingGVColumn, - this.purchaseDateGVColumn, - this.myRatingGVColumn, - this.miscGVColumn, - this.tagAndDetailsGVColumn}); + this.liberateGVColumn, + this.coverGVColumn, + this.titleGVColumn, + this.authorsGVColumn, + this.narratorsGVColumn, + this.lengthGVColumn, + this.seriesGVColumn, + this.descriptionGVColumn, + this.categoryGVColumn, + this.productRatingGVColumn, + this.purchaseDateGVColumn, + this.myRatingGVColumn, + this.miscGVColumn, + this.tagAndDetailsGVColumn}); this.gridEntryDataGridView.ContextMenuStrip = this.contextMenuStrip1; this.gridEntryDataGridView.DataSource = this.syncBindingSource; dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; @@ -214,7 +214,7 @@ // // syncBindingSource // - this.syncBindingSource.DataSource = typeof(LibationWinForms.GridEntry); + this.syncBindingSource.DataSource = typeof(LibationWinForms.GridView.GridEntry); // // ProductsGrid // diff --git a/Source/LibationWinForms/grid/ProductsGrid.cs b/Source/LibationWinForms/GridView/ProductsGrid.cs similarity index 99% rename from Source/LibationWinForms/grid/ProductsGrid.cs rename to Source/LibationWinForms/GridView/ProductsGrid.cs index 8e3cfe8d..5bf2a910 100644 --- a/Source/LibationWinForms/grid/ProductsGrid.cs +++ b/Source/LibationWinForms/GridView/ProductsGrid.cs @@ -8,7 +8,7 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; -namespace LibationWinForms.grid +namespace LibationWinForms.GridView { public partial class ProductsGrid : UserControl { diff --git a/Source/LibationWinForms/grid/ProductsGrid.resx b/Source/LibationWinForms/GridView/ProductsGrid.resx similarity index 100% rename from Source/LibationWinForms/grid/ProductsGrid.resx rename to Source/LibationWinForms/GridView/ProductsGrid.resx diff --git a/Source/LibationWinForms/grid/SeriesEntry.cs b/Source/LibationWinForms/GridView/SeriesEntry.cs similarity index 99% rename from Source/LibationWinForms/grid/SeriesEntry.cs rename to Source/LibationWinForms/GridView/SeriesEntry.cs index 9646c52f..17eb8125 100644 --- a/Source/LibationWinForms/grid/SeriesEntry.cs +++ b/Source/LibationWinForms/GridView/SeriesEntry.cs @@ -4,7 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; -namespace LibationWinForms +namespace LibationWinForms.GridView { public class SeriesEntry : GridEntry { diff --git a/Source/LibationWinForms/SyncBindingSource.cs b/Source/LibationWinForms/GridView/SyncBindingSource.cs similarity index 96% rename from Source/LibationWinForms/SyncBindingSource.cs rename to Source/LibationWinForms/GridView/SyncBindingSource.cs index 42d38ad9..5faafb0b 100644 --- a/Source/LibationWinForms/SyncBindingSource.cs +++ b/Source/LibationWinForms/GridView/SyncBindingSource.cs @@ -4,7 +4,7 @@ using System.Threading; using System.Windows.Forms; // https://stackoverflow.com/a/32886415 -namespace LibationWinForms +namespace LibationWinForms.GridView { public class SyncBindingSource : BindingSource { diff --git a/Source/LibationWinForms/Properties/DataSources/LibationWinForm.GridEntry.datasource b/Source/LibationWinForms/Properties/DataSources/LibationWinForm.GridEntry.datasource index ce0d9a71..48143915 100644 --- a/Source/LibationWinForms/Properties/DataSources/LibationWinForm.GridEntry.datasource +++ b/Source/LibationWinForms/Properties/DataSources/LibationWinForm.GridEntry.datasource @@ -6,5 +6,5 @@ cause the file to be unrecognizable by the program. --> - LibationWinForms.GridEntry, LibationWinForms, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null + LibationWinForms.GridView.GridEntry, LibationWinForms, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null \ No newline at end of file From 632b432b7c7f31bd9ee825c082fd1c6217cea81c Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Mon, 23 May 2022 22:20:54 -0600 Subject: [PATCH 10/15] Revert to old column indexing --- Source/LibationWinForms/Form1.PictureStorage.cs | 1 + Source/LibationWinForms/GridView/ProductsGrid.cs | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/LibationWinForms/Form1.PictureStorage.cs b/Source/LibationWinForms/Form1.PictureStorage.cs index 2a6efd09..c400eaf6 100644 --- a/Source/LibationWinForms/Form1.PictureStorage.cs +++ b/Source/LibationWinForms/Form1.PictureStorage.cs @@ -12,6 +12,7 @@ namespace LibationWinForms PictureStorage.SetDefaultImage(PictureSize._80x80, Properties.Resources.default_cover_80x80.ToBytes(format)); PictureStorage.SetDefaultImage(PictureSize._300x300, Properties.Resources.default_cover_300x300.ToBytes(format)); PictureStorage.SetDefaultImage(PictureSize._500x500, Properties.Resources.default_cover_500x500.ToBytes(format)); + PictureStorage.SetDefaultImage(PictureSize.Native, Properties.Resources.default_cover_500x500.ToBytes(format)); } } } diff --git a/Source/LibationWinForms/GridView/ProductsGrid.cs b/Source/LibationWinForms/GridView/ProductsGrid.cs index 5bf2a910..e9a3bdb1 100644 --- a/Source/LibationWinForms/GridView/ProductsGrid.cs +++ b/Source/LibationWinForms/GridView/ProductsGrid.cs @@ -54,16 +54,16 @@ namespace LibationWinForms.GridView var entry = getGridEntry(e.RowIndex); if (entry is LibraryBookEntry lbEntry) { - if (gridEntryDataGridView.Columns[e.ColumnIndex].DataPropertyName == liberateGVColumn.DataPropertyName) + if (e.ColumnIndex == liberateGVColumn.Index) LiberateClicked?.Invoke(lbEntry); - else if (gridEntryDataGridView.Columns[e.ColumnIndex].DataPropertyName == tagAndDetailsGVColumn.DataPropertyName && entry is LibraryBookEntry) + else if (e.ColumnIndex == tagAndDetailsGVColumn.Index && entry is LibraryBookEntry) DetailsClicked?.Invoke(lbEntry); - else if (gridEntryDataGridView.Columns[e.ColumnIndex].DataPropertyName == descriptionGVColumn.DataPropertyName) + else if (e.ColumnIndex == descriptionGVColumn.Index) DescriptionClicked?.Invoke(lbEntry, gridEntryDataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false)); - else if (gridEntryDataGridView.Columns[e.ColumnIndex].DataPropertyName == coverGVColumn.DataPropertyName) + else if (e.ColumnIndex == coverGVColumn.Index) CoverClicked?.Invoke(lbEntry); } - else if (entry is SeriesEntry sEntry && gridEntryDataGridView.Columns[e.ColumnIndex].DataPropertyName == liberateGVColumn.DataPropertyName) + else if (entry is SeriesEntry sEntry && e.ColumnIndex == liberateGVColumn.Index) { if (sEntry.Liberate.Expanded) bindingList.CollapseItem(sEntry); @@ -80,7 +80,6 @@ namespace LibationWinForms.GridView #endregion - #region UI display functions internal void BindToGrid(List dbBooks) From 43a4d0d1d7bb99f0ce653322ce232b1839973bb1 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Mon, 23 May 2022 22:24:45 -0600 Subject: [PATCH 11/15] Cleanup --- .../GridView/DescriptionDisplay.cs | 4 +- Source/LibationWinForms/GridView/GridEntry.cs | 14 +++---- .../LibationWinForms/GridView/ImageDisplay.cs | 4 +- .../LiberateDataGridViewImageButtonColumn.cs | 5 +-- .../GridView/LibraryBookEntry.cs | 10 ++--- .../GridView/ProductsDisplay.cs | 12 +++--- .../LibationWinForms/GridView/ProductsGrid.cs | 6 +-- .../LibationWinForms/GridView/SeriesEntry.cs | 16 ++++---- .../GridView/SyncBindingSource.cs | 37 +++++++++---------- 9 files changed, 50 insertions(+), 58 deletions(-) diff --git a/Source/LibationWinForms/GridView/DescriptionDisplay.cs b/Source/LibationWinForms/GridView/DescriptionDisplay.cs index a39790d9..d3aefd52 100644 --- a/Source/LibationWinForms/GridView/DescriptionDisplay.cs +++ b/Source/LibationWinForms/GridView/DescriptionDisplay.cs @@ -1,7 +1,5 @@ using System; -using System.ComponentModel; using System.Drawing; -using System.Linq; using System.Runtime.InteropServices; using System.Windows.Forms; @@ -11,7 +9,7 @@ namespace LibationWinForms.GridView { private int borderThickness = 5; - public int BorderThickness + public int BorderThickness { get => borderThickness; set diff --git a/Source/LibationWinForms/GridView/GridEntry.cs b/Source/LibationWinForms/GridView/GridEntry.cs index 733bcac8..6757ef60 100644 --- a/Source/LibationWinForms/GridView/GridEntry.cs +++ b/Source/LibationWinForms/GridView/GridEntry.cs @@ -81,21 +81,21 @@ namespace LibationWinForms.GridView ~GridEntry() { PictureStorage.PictureCached -= PictureStorage_PictureCached; - } + } } internal static class GridEntryExtensions { - #nullable enable - public static IEnumerable Series(this IEnumerable gridEntries) +#nullable enable + public static IEnumerable Series(this IEnumerable gridEntries) => gridEntries.Where(i => i is SeriesEntry).Cast(); - public static IEnumerable LibraryBooks(this IEnumerable gridEntries) + public static IEnumerable LibraryBooks(this IEnumerable gridEntries) => gridEntries.Where(i => i is LibraryBookEntry).Cast(); - public static LibraryBookEntry? FindBookByAsin(this IEnumerable gridEntries, string audibleProductID) + public static LibraryBookEntry? FindBookByAsin(this IEnumerable gridEntries, string audibleProductID) => gridEntries.FirstOrDefault(i => i.AudibleProductId == audibleProductID); - public static SeriesEntry? FindBookSeriesEntry(this IEnumerable gridEntries, IEnumerable matchSeries) + 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) + public static IEnumerable EmptySeries(this IEnumerable gridEntries) => gridEntries.Series().Where(i => i.Children.Count == 0); } } diff --git a/Source/LibationWinForms/GridView/ImageDisplay.cs b/Source/LibationWinForms/GridView/ImageDisplay.cs index a6bfa574..831b8ec9 100644 --- a/Source/LibationWinForms/GridView/ImageDisplay.cs +++ b/Source/LibationWinForms/GridView/ImageDisplay.cs @@ -1,6 +1,4 @@ -using FileLiberator; -using LibationFileManager; -using System; +using System; using System.Drawing; using System.IO; using System.Windows.Forms; diff --git a/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs b/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs index a237b7a0..f7291962 100644 --- a/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs +++ b/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs @@ -1,8 +1,7 @@ -using System; +using DataLayer; +using System; using System.Drawing; using System.Windows.Forms; -using System.Linq; -using DataLayer; namespace LibationWinForms.GridView { diff --git a/Source/LibationWinForms/GridView/LibraryBookEntry.cs b/Source/LibationWinForms/GridView/LibraryBookEntry.cs index 5fc606a1..8d93ebff 100644 --- a/Source/LibationWinForms/GridView/LibraryBookEntry.cs +++ b/Source/LibationWinForms/GridView/LibraryBookEntry.cs @@ -1,10 +1,10 @@ -using System; +using ApplicationServices; +using DataLayer; +using Dinah.Core; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; -using ApplicationServices; -using DataLayer; -using Dinah.Core; namespace LibationWinForms.GridView { @@ -183,7 +183,7 @@ namespace LibationWinForms.GridView { nameof(Liberate), () => Liberate }, { nameof(DateAdded), () => DateAdded }, }; - + #endregion diff --git a/Source/LibationWinForms/GridView/ProductsDisplay.cs b/Source/LibationWinForms/GridView/ProductsDisplay.cs index 66de694b..7e1b2d8a 100644 --- a/Source/LibationWinForms/GridView/ProductsDisplay.cs +++ b/Source/LibationWinForms/GridView/ProductsDisplay.cs @@ -1,14 +1,14 @@ -using System; +using ApplicationServices; +using DataLayer; +using FileLiberator; +using LibationFileManager; +using LibationWinForms.Dialogs; +using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; -using ApplicationServices; -using DataLayer; -using FileLiberator; -using LibationFileManager; -using LibationWinForms.Dialogs; namespace LibationWinForms.GridView { diff --git a/Source/LibationWinForms/GridView/ProductsGrid.cs b/Source/LibationWinForms/GridView/ProductsGrid.cs index e9a3bdb1..a715eb8e 100644 --- a/Source/LibationWinForms/GridView/ProductsGrid.cs +++ b/Source/LibationWinForms/GridView/ProductsGrid.cs @@ -46,17 +46,15 @@ namespace LibationWinForms.GridView private void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e) { // handle grid button click: https://stackoverflow.com/a/13687844 - if ( e.RowIndex < 0) + if (e.RowIndex < 0) return; - var column = gridEntryDataGridView.Columns[e.ColumnIndex]; - var entry = getGridEntry(e.RowIndex); if (entry is LibraryBookEntry lbEntry) { if (e.ColumnIndex == liberateGVColumn.Index) LiberateClicked?.Invoke(lbEntry); - else if (e.ColumnIndex == tagAndDetailsGVColumn.Index && entry is LibraryBookEntry) + else if (e.ColumnIndex == tagAndDetailsGVColumn.Index) DetailsClicked?.Invoke(lbEntry); else if (e.ColumnIndex == descriptionGVColumn.Index) DescriptionClicked?.Invoke(lbEntry, gridEntryDataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false)); diff --git a/Source/LibationWinForms/GridView/SeriesEntry.cs b/Source/LibationWinForms/GridView/SeriesEntry.cs index 17eb8125..65cb648e 100644 --- a/Source/LibationWinForms/GridView/SeriesEntry.cs +++ b/Source/LibationWinForms/GridView/SeriesEntry.cs @@ -31,14 +31,14 @@ namespace LibationWinForms.GridView } public override string Series { get; protected set; } public override string Title { get; protected set; } - public override string Length - { + public override string Length + { get { int bookLenMins = Children.Sum(c => c.LibraryBook.Book.LengthInMinutes); return bookLenMins == 0 ? "" : $"{bookLenMins / 60} hr {bookLenMins % 60} min"; - } - protected set => throw new NotImplementedException(); + } + protected set => throw new NotImplementedException(); } public override string Authors { get; protected set; } public override string Narrators { get; protected set; } @@ -57,7 +57,7 @@ namespace LibationWinForms.GridView public SeriesEntry(SeriesBook seriesBook, IEnumerable children) { - Children = children.Select(c=>new LibraryBookEntry(c) { Parent = this }).ToList(); + Children = children.Select(c => new LibraryBookEntry(c) { Parent = this }).ToList(); SetSeriesBook(seriesBook); } public SeriesEntry(SeriesBook seriesBook, LibraryBook child) @@ -96,9 +96,9 @@ namespace LibationWinForms.GridView { { nameof(Title), () => Book.SeriesSortable() }, { nameof(Series), () => Book.SeriesSortable() }, - { nameof(Length), () => Children.Sum(c=>c.LibraryBook.Book.LengthInMinutes) }, - { nameof(MyRating), () => Children.Average(c=>c.LibraryBook.Book.UserDefinedItem.Rating.FirstScore()) }, - { nameof(PurchaseDate), () => Children.Min(c=>c.LibraryBook.DateAdded) }, + { nameof(Length), () => Children.Sum(c => c.LibraryBook.Book.LengthInMinutes) }, + { nameof(MyRating), () => Children.Average(c => c.LibraryBook.Book.UserDefinedItem.Rating.FirstScore()) }, + { nameof(PurchaseDate), () => Children.Min(c => c.LibraryBook.DateAdded) }, { nameof(ProductRating), () => Children.Average(c => c.LibraryBook.Book.Rating.FirstScore()) }, { nameof(Authors), () => string.Empty }, { nameof(Narrators), () => string.Empty }, diff --git a/Source/LibationWinForms/GridView/SyncBindingSource.cs b/Source/LibationWinForms/GridView/SyncBindingSource.cs index 5faafb0b..970e110a 100644 --- a/Source/LibationWinForms/GridView/SyncBindingSource.cs +++ b/Source/LibationWinForms/GridView/SyncBindingSource.cs @@ -1,30 +1,29 @@ -using System; -using System.ComponentModel; +using System.ComponentModel; using System.Threading; using System.Windows.Forms; // https://stackoverflow.com/a/32886415 namespace LibationWinForms.GridView { - public class SyncBindingSource : BindingSource - { - private SynchronizationContext syncContext { get; } + public class SyncBindingSource : BindingSource + { + private SynchronizationContext syncContext { get; } - public SyncBindingSource() : base() - => syncContext = SynchronizationContext.Current; - public SyncBindingSource(IContainer container) : base(container) - => syncContext = SynchronizationContext.Current; - public SyncBindingSource(object dataSource, string dataMember) : base(dataSource, dataMember) - => syncContext = SynchronizationContext.Current; + public SyncBindingSource() : base() + => syncContext = SynchronizationContext.Current; + public SyncBindingSource(IContainer container) : base(container) + => syncContext = SynchronizationContext.Current; + public SyncBindingSource(object dataSource, string dataMember) : base(dataSource, dataMember) + => syncContext = SynchronizationContext.Current; - public override bool SupportsFiltering => true; + public override bool SupportsFiltering => true; protected override void OnListChanged(ListChangedEventArgs e) - { - if (syncContext is not null) - syncContext.Send(_ => base.OnListChanged(e), null); - else - base.OnListChanged(e); - } - } + { + if (syncContext is not null) + syncContext.Send(_ => base.OnListChanged(e), null); + else + base.OnListChanged(e); + } + } } From ee62d9ae8de52929d5513cade4ac250e1b9a6c4a Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Tue, 24 May 2022 07:36:17 -0600 Subject: [PATCH 12/15] Attempt to fix app hang on LogMe event --- Source/LibationWinForms/ProcessQueue/LogMe.cs | 30 +++++++++++++------ .../ProcessQueue/ProcessQueueControl.cs | 2 +- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Source/LibationWinForms/ProcessQueue/LogMe.cs b/Source/LibationWinForms/ProcessQueue/LogMe.cs index b6d61f69..06b84400 100644 --- a/Source/LibationWinForms/ProcessQueue/LogMe.cs +++ b/Source/LibationWinForms/ProcessQueue/LogMe.cs @@ -19,7 +19,7 @@ namespace LibationWinForms.ProcessQueue LogErrorString += (_, text) => Serilog.Log.Logger.Error(text); LogError += (_, tuple) => Serilog.Log.Logger.Error(tuple.Item1, tuple.Item2 ?? "Automated backup: error"); } - + private static ILogForm LogForm; public static LogMe RegisterForm(T form) where T : ILogForm { var logMe = new LogMe(); @@ -27,19 +27,31 @@ namespace LibationWinForms.ProcessQueue if (form is null) return logMe; - logMe.LogInfo += (_, text) => form?.WriteLine(text); + LogForm = form; - logMe.LogErrorString += (_, text) => form?.WriteLine(text); - - logMe.LogError += (_, tuple) => - { - form?.WriteLine(tuple.Item2 ?? "Automated backup: error"); - form?.WriteLine("ERROR: " + tuple.Item1.Message); - }; + logMe.LogInfo += LogMe_LogInfo; + logMe.LogErrorString += LogMe_LogErrorString; + logMe.LogError += LogMe_LogError; return logMe; } + private static async void LogMe_LogError(object sender, (Exception, string) tuple) + { + await Task.Run(() => LogForm?.WriteLine(tuple.Item2 ?? "Automated backup: error")); + await Task.Run(() => LogForm?.WriteLine("ERROR: " + tuple.Item1.Message)); + } + + private static async void LogMe_LogErrorString(object sender, string text) + { + await Task.Run(() => LogForm?.WriteLine(text)); + } + + private static async void LogMe_LogInfo(object sender, string text) + { + await Task.Run(() => LogForm?.WriteLine(text)); + } + public void Info(string text) => LogInfo?.Invoke(this, text); public void Error(string text) => LogErrorString?.Invoke(this, text); public void Error(Exception ex, string text = null) => LogError?.Invoke(this, (ex, text)); diff --git a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs index 9acf1eb4..f1830f51 100644 --- a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs +++ b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs @@ -169,7 +169,7 @@ namespace LibationWinForms.ProcessQueue if (IsDisposed) return; var timeStamp = DateTime.Now; - logDGV.Rows.Add(timeStamp, text.Trim()); + Invoke(() => logDGV.Rows.Add(timeStamp, text.Trim())); } #region Control event handlers From 43d6ea82cd11bb727cabe1ed6eeccb8cc41a11d5 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Tue, 24 May 2022 08:57:11 -0600 Subject: [PATCH 13/15] Change failure behavior to match previous implementation --- .../ProcessQueue/ProcessBook.cs | 1 - .../ProcessQueue/ProcessBookControl.cs | 4 +-- .../ProcessQueueControl.Designer.cs | 28 +++++++++---------- .../ProcessQueue/ProcessQueueControl.cs | 13 +++++---- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/Source/LibationWinForms/ProcessQueue/ProcessBook.cs b/Source/LibationWinForms/ProcessQueue/ProcessBook.cs index f84db515..7c9778a5 100644 --- a/Source/LibationWinForms/ProcessQueue/ProcessBook.cs +++ b/Source/LibationWinForms/ProcessQueue/ProcessBook.cs @@ -131,7 +131,6 @@ namespace LibationWinForms.ProcessQueue { ProcessBookResult.Success => ProcessBookStatus.Completed, ProcessBookResult.Cancelled => ProcessBookStatus.Cancelled, - ProcessBookResult.FailedRetry => ProcessBookStatus.Queued, _ => ProcessBookStatus.Failed, }; } diff --git a/Source/LibationWinForms/ProcessQueue/ProcessBookControl.cs b/Source/LibationWinForms/ProcessQueue/ProcessBookControl.cs index 7eee6913..62062468 100644 --- a/Source/LibationWinForms/ProcessQueue/ProcessBookControl.cs +++ b/Source/LibationWinForms/ProcessQueue/ProcessBookControl.cs @@ -72,8 +72,8 @@ namespace LibationWinForms.ProcessQueue Status = ProcessBookStatus.Cancelled; break; case ProcessBookResult.FailedRetry: - statusText = "Queued"; - Status = ProcessBookStatus.Queued; + statusText = "Error, will retry later"; + Status = ProcessBookStatus.Failed; break; case ProcessBookResult.FailedSkip: statusText = "Error, Skippping"; diff --git a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.Designer.cs b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.Designer.cs index 3e99394f..f600fd5d 100644 --- a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.Designer.cs +++ b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.Designer.cs @@ -51,9 +51,9 @@ this.logEntryColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.panel4 = new System.Windows.Forms.Panel(); this.panel2 = new System.Windows.Forms.Panel(); + this.logCopyBtn = new System.Windows.Forms.Button(); this.clearLogBtn = new System.Windows.Forms.Button(); this.counterTimer = new System.Windows.Forms.Timer(this.components); - this.logCopyBtn = new System.Windows.Forms.Button(); this.statusStrip1.SuspendLayout(); this.tabControl1.SuspendLayout(); this.tabPage1.SuspendLayout(); @@ -264,10 +264,21 @@ this.panel2.Size = new System.Drawing.Size(390, 25); this.panel2.TabIndex = 1; // + // logCopyBtn + // + this.logCopyBtn.Dock = System.Windows.Forms.DockStyle.Left; + this.logCopyBtn.Location = new System.Drawing.Point(0, 0); + this.logCopyBtn.Name = "logCopyBtn"; + this.logCopyBtn.Size = new System.Drawing.Size(57, 23); + this.logCopyBtn.TabIndex = 1; + this.logCopyBtn.Text = "Copy"; + this.logCopyBtn.UseVisualStyleBackColor = true; + this.logCopyBtn.Click += new System.EventHandler(this.LogCopyBtn_Click); + // // clearLogBtn // - this.clearLogBtn.Dock = System.Windows.Forms.DockStyle.Left; - this.clearLogBtn.Location = new System.Drawing.Point(0, 0); + this.clearLogBtn.Dock = System.Windows.Forms.DockStyle.Right; + this.clearLogBtn.Location = new System.Drawing.Point(328, 0); this.clearLogBtn.Name = "clearLogBtn"; this.clearLogBtn.Size = new System.Drawing.Size(60, 23); this.clearLogBtn.TabIndex = 0; @@ -280,17 +291,6 @@ this.counterTimer.Interval = 950; this.counterTimer.Tick += new System.EventHandler(this.CounterTimer_Tick); // - // logCopyBtn - // - this.logCopyBtn.Dock = System.Windows.Forms.DockStyle.Right; - this.logCopyBtn.Location = new System.Drawing.Point(331, 0); - this.logCopyBtn.Name = "logCopyBtn"; - this.logCopyBtn.Size = new System.Drawing.Size(57, 23); - this.logCopyBtn.TabIndex = 1; - this.logCopyBtn.Text = "Copy"; - this.logCopyBtn.UseVisualStyleBackColor = true; - this.logCopyBtn.Click += new System.EventHandler(this.LogCopyBtn_Click); - // // ProcessQueueControl // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); diff --git a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs index f1830f51..a55dc071 100644 --- a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs +++ b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs @@ -151,12 +151,15 @@ namespace LibationWinForms.ProcessQueue var result = await nextBook.ProcessOneAsync(); - if (result == ProcessBookResult.FailedRetry) - Queue.Enqueue(nextBook); - else if (result == ProcessBookResult.ValidationFail) + if (result == ProcessBookResult.ValidationFail) Queue.ClearCurrent(); else if (result == ProcessBookResult.FailedAbort) - return; + Queue.ClearQueue(); + else if (result == ProcessBookResult.FailedSkip) + { + nextBook.LibraryBook.Book.UserDefinedItem.BookStatus = DataLayer.LiberatedStatus.Error; + ApplicationServices.LibraryCommands.UpdateUserDefinedItem(nextBook.LibraryBook.Book); + } } Queue_CompletedCountChanged(this, 0); counterTimer.Stop(); @@ -176,7 +179,7 @@ namespace LibationWinForms.ProcessQueue private void Queue_CompletedCountChanged(object sender, int e) { - int errCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.FailedAbort or ProcessBookResult.FailedSkip or ProcessBookResult.ValidationFail); + int errCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.FailedAbort or ProcessBookResult.FailedSkip or ProcessBookResult.FailedRetry or ProcessBookResult.ValidationFail); int completeCount = Queue.Completed.Count(p => p.Result is ProcessBookResult.Success); ErrorCount = errCount; From e26deb90923dda188a52dc6c1ebdabcfc5f5d67d Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Tue, 24 May 2022 11:15:41 -0600 Subject: [PATCH 14/15] 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() }, From 68735a45dd4f90229f4ade1243bc8d83f5a9265e Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Tue, 24 May 2022 11:52:33 -0600 Subject: [PATCH 15/15] Change episode color --- .../GridView/LiberateDataGridViewImageButtonColumn.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs b/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs index b7f0eaa7..62c56538 100644 --- a/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs +++ b/Source/LibationWinForms/GridView/LiberateDataGridViewImageButtonColumn.cs @@ -16,7 +16,7 @@ namespace LibationWinForms.GridView internal class LiberateDataGridViewImageButtonCell : DataGridViewImageButtonCell { - private static readonly Color SERIES_BG_COLOR = Color.LightSkyBlue; + private static readonly Color SERIES_BG_COLOR = Color.FromArgb(230, 255, 230); 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);