From 52193933b2fdf5594b4faf2330dcc293b61eaa96 Mon Sep 17 00:00:00 2001 From: Michael Bucari-Tovo Date: Fri, 10 Jun 2022 19:22:54 -0600 Subject: [PATCH] Add scan and remove books tomain view, remove separate dialog. --- Source/ApplicationServices/LibraryCommands.cs | 2 +- .../Dialogs/AccountsDialog.cs | 24 ++- .../Dialogs/RemoveBooksDialog.Designer.cs | 189 ------------------ .../Dialogs/RemoveBooksDialog.cs | 149 -------------- .../Dialogs/RemoveBooksDialog.resx | 63 ------ Source/LibationWinForms/Form1.Designer.cs | 15 +- Source/LibationWinForms/Form1.ScanManual.cs | 50 ++++- .../Form1.ScanNotification.cs | 6 + Source/LibationWinForms/Form1.resx | 36 ---- Source/LibationWinForms/GridView/GridEntry.cs | 9 + .../GridView/LibraryBookEntry.cs | 14 ++ .../GridView/ProductsDisplay.Designer.cs | 3 +- .../GridView/ProductsDisplay.cs | 62 ++++++ .../GridView/ProductsGrid.Designer.cs | 71 ++++--- .../LibationWinForms/GridView/ProductsGrid.cs | 36 ++++ .../GridView/ProductsGrid.resx | 9 +- .../LibationWinForms/GridView/SeriesEntry.cs | 36 ++++ 17 files changed, 289 insertions(+), 485 deletions(-) delete mode 100644 Source/LibationWinForms/Dialogs/RemoveBooksDialog.Designer.cs delete mode 100644 Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs delete mode 100644 Source/LibationWinForms/Dialogs/RemoveBooksDialog.resx diff --git a/Source/ApplicationServices/LibraryCommands.cs b/Source/ApplicationServices/LibraryCommands.cs index 23c5dd7d..42f751e3 100644 --- a/Source/ApplicationServices/LibraryCommands.cs +++ b/Source/ApplicationServices/LibraryCommands.cs @@ -27,7 +27,7 @@ namespace ApplicationServices ScanEnd += (_, __) => Scanning = false; } - public static async Task> FindInactiveBooks(Func> apiExtendedfunc, List existingLibrary, params Account[] accounts) + public static async Task> FindInactiveBooks(Func> apiExtendedfunc, IEnumerable existingLibrary, params Account[] accounts) { logRestart(); diff --git a/Source/LibationWinForms/Dialogs/AccountsDialog.cs b/Source/LibationWinForms/Dialogs/AccountsDialog.cs index 0169ed0f..f9361f22 100644 --- a/Source/LibationWinForms/Dialogs/AccountsDialog.cs +++ b/Source/LibationWinForms/Dialogs/AccountsDialog.cs @@ -151,13 +151,13 @@ namespace LibationWinForms.Dialogs { if (string.IsNullOrWhiteSpace(dto.AccountId)) { - MessageBox.Show("Account id cannot be blank. Please enter an account id for all accounts.", "Blank account", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show(this, "Account id cannot be blank. Please enter an account id for all accounts.", "Blank account", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } if (string.IsNullOrWhiteSpace(dto.LocaleName)) { - MessageBox.Show("Please select a locale (i.e.: country or region) for all accounts.", "Blank region", MessageBoxButtons.OK, MessageBoxIcon.Error); + MessageBox.Show(this, "Please select a locale (i.e.: country or region) for all accounts.", "Blank region", MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } } @@ -222,7 +222,7 @@ namespace LibationWinForms.Dialogs if (account.IdentityTokens?.IsValid != true) { - MessageBox.Show("This account hasn't been authenticated yet. First scan your library to log into your account, then try exporting again.", "Account Not Authenticated"); + MessageBox.Show(this, "This account hasn't been authenticated yet. First scan your library to log into your account, then try exporting again.", "Account Not Authenticated"); return; } @@ -238,12 +238,15 @@ namespace LibationWinForms.Dialogs File.WriteAllText(sfd.FileName, jsonText); - MessageBox.Show($"Successfully exported {account.AccountName} to\r\n\r\n{sfd.FileName}", "Success!"); + MessageBox.Show(this, $"Successfully exported {account.AccountName} to\r\n\r\n{sfd.FileName}", "Success!"); } catch (Exception ex) { - Serilog.Log.Logger.Error(ex, "Unable to export account: {0}", account); - MessageBox.Show($"An error occured while exporting account:\r\n{account.AccountName}", "Error Exporting Account"); + MessageBoxLib.ShowAdminAlert( + this, + $"An error occured while exporting account:\r\n{account.AccountName}", + "Error Exporting Account", + ex); } } private async void importBtn_Click(object sender, EventArgs e) @@ -264,7 +267,7 @@ namespace LibationWinForms.Dialogs if (persister.AccountsSettings.Accounts.Any(a => a.AccountId == account.AccountId && a.IdentityTokens.Locale.Name == account.Locale.Name)) { - MessageBox.Show($"An account with that account id and country already exists.\r\n\r\nAccount ID: {account.AccountId}\r\nCountry: {account.Locale.Name}", "Cannot Add Duplicate Account"); + MessageBox.Show(this, $"An account with that account id and country already exists.\r\n\r\nAccount ID: {account.AccountId}\r\nCountry: {account.Locale.Name}", "Cannot Add Duplicate Account"); return; } @@ -274,8 +277,11 @@ namespace LibationWinForms.Dialogs } catch (Exception ex) { - Serilog.Log.Logger.Error(ex, "Unable to import audible-cli auth file: {0}", ofd.FileName); - MessageBox.Show($"An error occured while importing an account from:\r\n{ofd.FileName}\r\n\r\nIs the file encrypted?", "Error Importing Account"); + MessageBoxLib.ShowAdminAlert( + this, + $"An error occured while importing an account from:\r\n{ofd.FileName}\r\n\r\nIs the file encrypted?", + "Error Importing Account", + ex); } } } diff --git a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.Designer.cs b/Source/LibationWinForms/Dialogs/RemoveBooksDialog.Designer.cs deleted file mode 100644 index 7240b8f3..00000000 --- a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.Designer.cs +++ /dev/null @@ -1,189 +0,0 @@ - -namespace LibationWinForms.Dialogs -{ - partial class RemoveBooksDialog - { - /// - /// 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 Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); - this._dataGridView = new System.Windows.Forms.DataGridView(); - this.removeDataGridViewCheckBoxColumn = new System.Windows.Forms.DataGridViewCheckBoxColumn(); - this.coverDataGridViewImageColumn = new System.Windows.Forms.DataGridViewImageColumn(); - this.titleDataGridViewTextBoxColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); - 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.GridView.SyncBindingSource(this.components); - this.btnRemoveBooks = new System.Windows.Forms.Button(); - this.label1 = new System.Windows.Forms.Label(); - ((System.ComponentModel.ISupportInitialize)(this._dataGridView)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).BeginInit(); - this.SuspendLayout(); - // - // _dataGridView - // - this._dataGridView.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._dataGridView.AutoGenerateColumns = false; - this._dataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; - this._dataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { - this.removeDataGridViewCheckBoxColumn, - this.coverDataGridViewImageColumn, - this.titleDataGridViewTextBoxColumn, - this.authorsDataGridViewTextBoxColumn, - this.miscDataGridViewTextBoxColumn, - this.purchaseDateGridViewTextBoxColumn}); - this._dataGridView.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); - 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._dataGridView.DefaultCellStyle = dataGridViewCellStyle1; - this._dataGridView.Location = new System.Drawing.Point(0, 0); - this._dataGridView.Name = "_dataGridView"; - this._dataGridView.RowHeadersVisible = false; - this._dataGridView.RowTemplate.Height = 82; - this._dataGridView.Size = new System.Drawing.Size(730, 409); - this._dataGridView.TabIndex = 0; - // - // removeDataGridViewCheckBoxColumn - // - this.removeDataGridViewCheckBoxColumn.DataPropertyName = "Remove"; - this.removeDataGridViewCheckBoxColumn.FalseValue = "False"; - this.removeDataGridViewCheckBoxColumn.Frozen = true; - this.removeDataGridViewCheckBoxColumn.HeaderText = "Remove"; - this.removeDataGridViewCheckBoxColumn.MinimumWidth = 80; - this.removeDataGridViewCheckBoxColumn.Name = "removeDataGridViewCheckBoxColumn"; - this.removeDataGridViewCheckBoxColumn.Resizable = System.Windows.Forms.DataGridViewTriState.False; - this.removeDataGridViewCheckBoxColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; - this.removeDataGridViewCheckBoxColumn.TrueValue = "True"; - this.removeDataGridViewCheckBoxColumn.Width = 80; - // - // coverDataGridViewImageColumn - // - this.coverDataGridViewImageColumn.DataPropertyName = "Cover"; - this.coverDataGridViewImageColumn.HeaderText = "Cover"; - this.coverDataGridViewImageColumn.MinimumWidth = 80; - this.coverDataGridViewImageColumn.Name = "coverDataGridViewImageColumn"; - this.coverDataGridViewImageColumn.ReadOnly = true; - this.coverDataGridViewImageColumn.Resizable = System.Windows.Forms.DataGridViewTriState.False; - this.coverDataGridViewImageColumn.Width = 80; - // - // titleDataGridViewTextBoxColumn - // - this.titleDataGridViewTextBoxColumn.DataPropertyName = "Title"; - this.titleDataGridViewTextBoxColumn.HeaderText = "Title"; - this.titleDataGridViewTextBoxColumn.Name = "titleDataGridViewTextBoxColumn"; - this.titleDataGridViewTextBoxColumn.ReadOnly = true; - this.titleDataGridViewTextBoxColumn.Width = 200; - // - // authorsDataGridViewTextBoxColumn - // - this.authorsDataGridViewTextBoxColumn.DataPropertyName = "Authors"; - this.authorsDataGridViewTextBoxColumn.HeaderText = "Authors"; - this.authorsDataGridViewTextBoxColumn.Name = "authorsDataGridViewTextBoxColumn"; - this.authorsDataGridViewTextBoxColumn.ReadOnly = true; - // - // miscDataGridViewTextBoxColumn - // - this.miscDataGridViewTextBoxColumn.DataPropertyName = "Misc"; - this.miscDataGridViewTextBoxColumn.HeaderText = "Misc"; - this.miscDataGridViewTextBoxColumn.Name = "miscDataGridViewTextBoxColumn"; - this.miscDataGridViewTextBoxColumn.ReadOnly = true; - this.miscDataGridViewTextBoxColumn.Width = 150; - // - // purchaseDateGridViewTextBoxColumn - // - this.purchaseDateGridViewTextBoxColumn.DataPropertyName = "PurchaseDate"; - this.purchaseDateGridViewTextBoxColumn.HeaderText = "Purchase Date"; - this.purchaseDateGridViewTextBoxColumn.Name = "purchaseDateGridViewTextBoxColumn"; - this.purchaseDateGridViewTextBoxColumn.ReadOnly = true; - this.purchaseDateGridViewTextBoxColumn.Resizable = System.Windows.Forms.DataGridViewTriState.False; - // - // gridEntryBindingSource - // - this.gridEntryBindingSource.AllowNew = false; - this.gridEntryBindingSource.DataSource = typeof(LibationWinForms.Dialogs.RemovableGridEntry); - // - // btnRemoveBooks - // - this.btnRemoveBooks.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.btnRemoveBooks.Location = new System.Drawing.Point(500, 419); - this.btnRemoveBooks.Name = "btnRemoveBooks"; - this.btnRemoveBooks.Size = new System.Drawing.Size(218, 23); - this.btnRemoveBooks.TabIndex = 1; - this.btnRemoveBooks.Text = "Remove Selected Books from Libation"; - this.btnRemoveBooks.UseVisualStyleBackColor = true; - this.btnRemoveBooks.Click += new System.EventHandler(this.btnRemoveBooks_Click); - // - // label1 - // - this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(12, 423); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(178, 15); - this.label1.TabIndex = 2; - this.label1.Text = "{0} book{1} selected for removal."; - // - // RemoveBooksDialog - // - this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(730, 450); - this.Controls.Add(this.label1); - this.Controls.Add(this.btnRemoveBooks); - this.Controls.Add(this._dataGridView); - this.Name = "RemoveBooksDialog"; - this.Text = "Remove Books from Libation's Database"; - this.Shown += new System.EventHandler(this.RemoveBooksDialog_Shown); - ((System.ComponentModel.ISupportInitialize)(this._dataGridView)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).EndInit(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.DataGridView _dataGridView; - private LibationWinForms.GridView.SyncBindingSource gridEntryBindingSource; - private System.Windows.Forms.Button btnRemoveBooks; - private System.Windows.Forms.Label label1; - private System.Windows.Forms.DataGridViewCheckBoxColumn removeDataGridViewCheckBoxColumn; - private System.Windows.Forms.DataGridViewImageColumn coverDataGridViewImageColumn; - private System.Windows.Forms.DataGridViewTextBoxColumn titleDataGridViewTextBoxColumn; - private System.Windows.Forms.DataGridViewTextBoxColumn authorsDataGridViewTextBoxColumn; - private System.Windows.Forms.DataGridViewTextBoxColumn miscDataGridViewTextBoxColumn; - private System.Windows.Forms.DataGridViewTextBoxColumn purchaseDateGridViewTextBoxColumn; - } -} \ No newline at end of file diff --git a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs b/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs deleted file mode 100644 index 84e5ff51..00000000 --- a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Linq; -using System.Windows.Forms; -using ApplicationServices; -using AudibleUtilities; -using DataLayer; -using Dinah.Core.DataBinding; -using LibationFileManager; -using LibationWinForms.Login; - -namespace LibationWinForms.Dialogs -{ - public partial class RemoveBooksDialog : Form - { - private Account[] _accounts { get; } - private List _libraryBooks { get; } - private SortableBindingList _removableGridEntries { get; } - private string _labelFormat { get; } - private int SelectedCount => SelectedEntries?.Count() ?? 0; - private IEnumerable SelectedEntries => _removableGridEntries?.Where(b => b.Remove); - - public RemoveBooksDialog(params Account[] accounts) - { - _libraryBooks = DbContexts.GetLibrary_Flat_NoTracking(); - _accounts = accounts; - - InitializeComponent(); - - this.Load += (_, _) => this.RestoreSizeAndLocation(Configuration.Instance); - this.FormClosing += (_, _) => this.SaveSizeAndLocation(Configuration.Instance); - - _labelFormat = label1.Text; - - _dataGridView.CellContentClick += (_, _) => _dataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit); - _dataGridView.CellValueChanged += (_, _) => UpdateSelection(); - _dataGridView.BindingContextChanged += _dataGridView_BindingContextChanged; - - var orderedGridEntries = _libraryBooks - .Select(lb => new RemovableGridEntry(lb)) - .OrderByDescending(ge => (DateTime)ge.GetMemberValue(nameof(ge.PurchaseDate))) - .ToList(); - - _removableGridEntries = new SortableBindingList(orderedGridEntries); - gridEntryBindingSource.DataSource = _removableGridEntries; - - _dataGridView.Enabled = false; - this.SetLibationIcon(); - } - - private void _dataGridView_BindingContextChanged(object sender, EventArgs e) - { - _dataGridView.Sort(_dataGridView.Columns[0], ListSortDirection.Descending); - UpdateSelection(); - } - - private async void RemoveBooksDialog_Shown(object sender, EventArgs e) - { - if (_accounts is null || _accounts.Length == 0) - return; - try - { - var removedBooks = await LibraryCommands.FindInactiveBooks(WinformLoginChoiceEager.ApiExtendedFunc, _libraryBooks, _accounts); - - var removable = _removableGridEntries.Where(rge => removedBooks.Any(rb => rb.Book.AudibleProductId == rge.AudibleProductId)).ToList(); - - if (!removable.Any()) - return; - - foreach (var r in removable) - r.Remove = true; - - UpdateSelection(); - } - catch (Exception ex) - { - MessageBoxLib.ShowAdminAlert( - this, - "Error scanning library. You may still manually select books to remove from Libation's library.", - "Error scanning library", - ex); - } - finally - { - _dataGridView.Enabled = true; - } - } - - private async void btnRemoveBooks_Click(object sender, EventArgs e) - { - var selectedBooks = SelectedEntries.ToList(); - - if (selectedBooks.Count == 0) - return; - - var libraryBooks = selectedBooks.Select(rge => rge.LibraryBook).ToList(); - var result = MessageBoxLib.ShowConfirmationDialog( - libraryBooks, - $"Are you sure you want to remove {0} from Libation's library?", - "Remove books from Libation?"); - - if (result != DialogResult.Yes) - return; - - var idsToRemove = libraryBooks.Select(lb => lb.Book.AudibleProductId).ToList(); - var removeLibraryBooks = await LibraryCommands.RemoveBooksAsync(idsToRemove); - - foreach (var rEntry in selectedBooks) - _removableGridEntries.Remove(rEntry); - - UpdateSelection(); - } - - private void UpdateSelection() - { - var selectedCount = SelectedCount; - label1.Text = string.Format(_labelFormat, selectedCount, selectedCount != 1 ? "s" : string.Empty); - btnRemoveBooks.Enabled = selectedCount > 0; - } - } - - internal class RemovableGridEntry : GridView.LibraryBookEntry - { - private bool _remove = false; - public RemovableGridEntry(LibraryBook libraryBook) : base(libraryBook) { } - - public bool Remove - { - get - { - return _remove; - } - set - { - _remove = value; - NotifyPropertyChanged(); - } - } - - public override object GetMemberValue(string memberName) - { - if (memberName == nameof(Remove)) - return Remove; - return base.GetMemberValue(memberName); - } - } -} diff --git a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.resx b/Source/LibationWinForms/Dialogs/RemoveBooksDialog.resx deleted file mode 100644 index a3058bc8..00000000 --- a/Source/LibationWinForms/Dialogs/RemoveBooksDialog.resx +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 17, 17 - - \ No newline at end of file diff --git a/Source/LibationWinForms/Form1.Designer.cs b/Source/LibationWinForms/Form1.Designer.cs index c670c6d3..bcd89f8b 100644 --- a/Source/LibationWinForms/Form1.Designer.cs +++ b/Source/LibationWinForms/Form1.Designer.cs @@ -42,6 +42,7 @@ this.removeLibraryBooksToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.removeAllAccountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.removeSomeAccountsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.closeRemoveBooksColumnToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.liberateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.beginBookBackupsToolStripMenuItem = new LibationWinForms.FormattableToolStripMenuItem(); this.beginPdfBackupsToolStripMenuItem = new LibationWinForms.FormattableToolStripMenuItem(); @@ -144,7 +145,8 @@ this.scanLibraryToolStripMenuItem, this.scanLibraryOfAllAccountsToolStripMenuItem, this.scanLibraryOfSomeAccountsToolStripMenuItem, - this.removeLibraryBooksToolStripMenuItem}); + this.removeLibraryBooksToolStripMenuItem, + this.closeRemoveBooksColumnToolStripMenuItem}); this.importToolStripMenuItem.Name = "importToolStripMenuItem"; this.importToolStripMenuItem.Size = new System.Drawing.Size(55, 20); this.importToolStripMenuItem.Text = "&Import"; @@ -209,6 +211,14 @@ this.removeSomeAccountsToolStripMenuItem.Text = "Some Accounts"; this.removeSomeAccountsToolStripMenuItem.Click += new System.EventHandler(this.removeSomeAccountsToolStripMenuItem_Click); // + // closeRemoveBooksColumnToolStripMenuItem + // + this.closeRemoveBooksColumnToolStripMenuItem.Name = "closeRemoveBooksColumnToolStripMenuItem"; + this.closeRemoveBooksColumnToolStripMenuItem.Size = new System.Drawing.Size(247, 22); + this.closeRemoveBooksColumnToolStripMenuItem.Text = "&Done Removing Books"; + this.closeRemoveBooksColumnToolStripMenuItem.Visible = false; + this.closeRemoveBooksColumnToolStripMenuItem.Click += new System.EventHandler(this.closeRemoveBooksColumnToolStripMenuItem_Click); + // // liberateToolStripMenuItem // this.liberateToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -396,6 +406,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.ShowItemToolTips = true; this.statusStrip1.Size = new System.Drawing.Size(1061, 22); this.statusStrip1.TabIndex = 6; this.statusStrip1.Text = "statusStrip1"; @@ -485,6 +496,7 @@ this.productsDisplay.Size = new System.Drawing.Size(1031, 555); this.productsDisplay.TabIndex = 9; this.productsDisplay.VisibleCountChanged += new System.EventHandler(this.productsDisplay_VisibleCountChanged); + this.productsDisplay.RemovableCountChanged += new System.EventHandler(this.productsDisplay_RemovableCountChanged); this.productsDisplay.LiberateClicked += new System.EventHandler(this.ProductsDisplay_LiberateClicked); this.productsDisplay.InitialLoaded += new System.EventHandler(this.productsDisplay_InitialLoaded); // @@ -584,5 +596,6 @@ private System.Windows.Forms.Panel panel1; private System.Windows.Forms.Button toggleQueueHideBtn; private LibationWinForms.GridView.ProductsDisplay productsDisplay; + private System.Windows.Forms.ToolStripMenuItem closeRemoveBooksColumnToolStripMenuItem; } } diff --git a/Source/LibationWinForms/Form1.ScanManual.cs b/Source/LibationWinForms/Form1.ScanManual.cs index 4b9215d9..1228979a 100644 --- a/Source/LibationWinForms/Form1.ScanManual.cs +++ b/Source/LibationWinForms/Form1.ScanManual.cs @@ -13,10 +13,27 @@ namespace LibationWinForms // this is for manual scan/import. Unrelated to auto-scan public partial class Form1 { + + private ToolStripButton removeCheckedBtn = new(); private void Configure_ScanManual() { this.Load += refreshImportMenu; AccountsSettingsPersister.Saved += refreshImportMenu; + + #region Create and Add Tool Strip Button + removeCheckedBtn.DisplayStyle = ToolStripItemDisplayStyle.Text; + removeCheckedBtn.Name = "removeSelectedBtn"; + removeCheckedBtn.Text = "Remove 0 Books"; + removeCheckedBtn.AutoToolTip = false; + removeCheckedBtn.ToolTipText = "Remove checked books and series\r\nfrom Libation's database.\r\n\r\nThey will remain in your Audible account."; + removeCheckedBtn.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; + removeCheckedBtn.Alignment = ToolStripItemAlignment.Left; + removeCheckedBtn.Anchor = AnchorStyles.Bottom | AnchorStyles.Left; + removeCheckedBtn.Font = new System.Drawing.Font(removeCheckedBtn.Font, System.Drawing.FontStyle.Bold); + removeCheckedBtn.Click += (_, _) => productsDisplay.RemoveCheckedBooksAsync(); + removeCheckedBtn.Visible = false; + statusStrip1.Items.Insert(1, removeCheckedBtn); + #endregion } private void refreshImportMenu(object _, EventArgs __) @@ -106,10 +123,37 @@ namespace LibationWinForms scanLibrariesRemovedBooks(scanAccountsDialog.CheckedAccounts.ToArray()); } - private void scanLibrariesRemovedBooks(params Account[] accounts) + private async void scanLibrariesRemovedBooks(params Account[] accounts) { - using var dialog = new RemoveBooksDialog(accounts); - dialog.ShowDialog(); + //This action is meant to operate on the entire library. + //For removing books within a filter set, use + //Visible Books > Remove from library + filterSearchTb.Enabled = false; + productsDisplay.Filter(null); + + removeCheckedBtn.Visible = true; + closeRemoveBooksColumnToolStripMenuItem.Visible = true; + await productsDisplay.ScanAndRemoveBooksAsync(accounts); + } + + private void closeRemoveBooksColumnToolStripMenuItem_Click(object sender, EventArgs e) + { + removeCheckedBtn.Visible = false; + closeRemoveBooksColumnToolStripMenuItem.Visible = false; + productsDisplay.CloseRemoveBooksColumn(); + + //Restore the filter + filterSearchTb.Enabled = true; + performFilter(filterSearchTb.Text); + } + + private void productsDisplay_RemovableCountChanged(object sender, int removeCount) + { + removeCheckedBtn.Text = removeCount switch + { + 1 => "Remove 1 Book", + _ => $"Remove {removeCount} Books" + }; } private async Task scanLibrariesAsync(IEnumerable accounts) => await scanLibrariesAsync(accounts.ToArray()); diff --git a/Source/LibationWinForms/Form1.ScanNotification.cs b/Source/LibationWinForms/Form1.ScanNotification.cs index d6c5fa10..dc70537d 100644 --- a/Source/LibationWinForms/Form1.ScanNotification.cs +++ b/Source/LibationWinForms/Form1.ScanNotification.cs @@ -14,6 +14,9 @@ namespace LibationWinForms private void LibraryCommands_ScanBegin(object sender, int accountsLength) { + removeLibraryBooksToolStripMenuItem.Enabled = false; + removeAllAccountsToolStripMenuItem.Enabled = false; + removeSomeAccountsToolStripMenuItem.Enabled = false; scanLibraryToolStripMenuItem.Enabled = false; scanLibraryOfAllAccountsToolStripMenuItem.Enabled = false; scanLibraryOfSomeAccountsToolStripMenuItem.Enabled = false; @@ -27,6 +30,9 @@ namespace LibationWinForms private void LibraryCommands_ScanEnd(object sender, EventArgs e) { + removeLibraryBooksToolStripMenuItem.Enabled = true; + removeAllAccountsToolStripMenuItem.Enabled = true; + removeSomeAccountsToolStripMenuItem.Enabled = true; scanLibraryToolStripMenuItem.Enabled = true; scanLibraryOfAllAccountsToolStripMenuItem.Enabled = true; scanLibraryOfSomeAccountsToolStripMenuItem.Enabled = true; diff --git a/Source/LibationWinForms/Form1.resx b/Source/LibationWinForms/Form1.resx index 2505fa27..64da6d15 100644 --- a/Source/LibationWinForms/Form1.resx +++ b/Source/LibationWinForms/Form1.resx @@ -57,48 +57,12 @@ 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/GridView/GridEntry.cs b/Source/LibationWinForms/GridView/GridEntry.cs index 8e5702e1..a9a27c3a 100644 --- a/Source/LibationWinForms/GridView/GridEntry.cs +++ b/Source/LibationWinForms/GridView/GridEntry.cs @@ -12,6 +12,12 @@ using System.Linq; namespace LibationWinForms.GridView { + public enum RemoveStatus + { + NotRemoved, + Removed, + SomeRemoved + } /// The View Model base for the DataGridView public abstract class GridEntry : AsyncNotifyPropertyChanged, IMemberComparable { @@ -24,6 +30,9 @@ namespace LibationWinForms.GridView #region Model properties exposed to the view + protected RemoveStatus _remove = RemoveStatus.NotRemoved; + public abstract RemoveStatus Remove { get; set; } + public abstract LiberateButtonStatus Liberate { get; } public Image Cover { diff --git a/Source/LibationWinForms/GridView/LibraryBookEntry.cs b/Source/LibationWinForms/GridView/LibraryBookEntry.cs index 861b9587..85e6c687 100644 --- a/Source/LibationWinForms/GridView/LibraryBookEntry.cs +++ b/Source/LibationWinForms/GridView/LibraryBookEntry.cs @@ -20,6 +20,20 @@ namespace LibationWinForms.GridView private LiberatedStatus _bookStatus; private LiberatedStatus? _pdfStatus; + public override RemoveStatus Remove + { + get + { + return _remove; + } + set + { + _remove = value is RemoveStatus.SomeRemoved ? RemoveStatus.NotRemoved : value; + Parent?.ChildRemoveUpdate(); + NotifyPropertyChanged(); + } + } + public override LiberateButtonStatus Liberate { get diff --git a/Source/LibationWinForms/GridView/ProductsDisplay.Designer.cs b/Source/LibationWinForms/GridView/ProductsDisplay.Designer.cs index db9ca6a5..abd5823e 100644 --- a/Source/LibationWinForms/GridView/ProductsDisplay.Designer.cs +++ b/Source/LibationWinForms/GridView/ProductsDisplay.Designer.cs @@ -39,11 +39,12 @@ this.productsGrid.Name = "productsGrid"; this.productsGrid.Size = new System.Drawing.Size(1510, 380); this.productsGrid.TabIndex = 0; + this.productsGrid.VisibleCountChanged += new System.EventHandler(this.productsGrid_VisibleCountChanged); this.productsGrid.LiberateClicked += new LibationWinForms.GridView.LibraryBookEntryClickedEventHandler(this.productsGrid_LiberateClicked); this.productsGrid.CoverClicked += new LibationWinForms.GridView.GridEntryClickedEventHandler(this.productsGrid_CoverClicked); this.productsGrid.DetailsClicked += new LibationWinForms.GridView.LibraryBookEntryClickedEventHandler(this.productsGrid_DetailsClicked); this.productsGrid.DescriptionClicked += new LibationWinForms.GridView.GridEntryRectangleClickedEventHandler(this.productsGrid_DescriptionClicked); - this.productsGrid.VisibleCountChanged += new System.EventHandler(this.productsGrid_VisibleCountChanged); + this.productsGrid.RemovableCountChanged += new LibationWinForms.GridView.GridEntryClickedEventHandler(this.productsGrid_RemovableCountChanged); // // ProductsDisplay // diff --git a/Source/LibationWinForms/GridView/ProductsDisplay.cs b/Source/LibationWinForms/GridView/ProductsDisplay.cs index 7eb55455..a86657c5 100644 --- a/Source/LibationWinForms/GridView/ProductsDisplay.cs +++ b/Source/LibationWinForms/GridView/ProductsDisplay.cs @@ -1,4 +1,5 @@ using ApplicationServices; +using AudibleUtilities; using DataLayer; using FileLiberator; using LibationFileManager; @@ -16,6 +17,7 @@ namespace LibationWinForms.GridView { /// Number of visible rows has changed public event EventHandler VisibleCountChanged; + public event EventHandler RemovableCountChanged; public event EventHandler LiberateClicked; public event EventHandler InitialLoaded; @@ -82,6 +84,61 @@ namespace LibationWinForms.GridView #region UI display functions + public void CloseRemoveBooksColumn() + => productsGrid.RemoveColumnVisible = false; + + public async void RemoveCheckedBooksAsync() + { + var selectedBooks = productsGrid.GetAllBookEntries().Where(lbe => lbe.Remove is RemoveStatus.Removed).ToList(); + + if (selectedBooks.Count == 0) + return; + + var libraryBooks = selectedBooks.Select(rge => rge.LibraryBook).ToList(); + var result = MessageBoxLib.ShowConfirmationDialog( + libraryBooks, + $"Are you sure you want to remove {selectedBooks.Count} books from Libation's library?", + "Remove books from Libation?"); + + if (result != DialogResult.Yes) + return; + + productsGrid.RemoveBooks(selectedBooks); + var idsToRemove = libraryBooks.Select(lb => lb.Book.AudibleProductId).ToList(); + var removeLibraryBooks = await LibraryCommands.RemoveBooksAsync(idsToRemove); + } + + public async Task ScanAndRemoveBooksAsync(params Account[] accounts) + { + RemovableCountChanged?.Invoke(this, 0); + productsGrid.RemoveColumnVisible = true; + + try + { + if (accounts is null || accounts.Length == 0) + return; + + var allBooks = productsGrid.GetAllBookEntries(); + var lib = allBooks.Select(lbe => lbe.LibraryBook); + var removedBooks = await LibraryCommands.FindInactiveBooks(Login.WinformLoginChoiceEager.ApiExtendedFunc, lib, accounts); + + var removable = allBooks.Where(lbe => removedBooks.Any(rb => rb.Book.AudibleProductId == lbe.AudibleProductId)).ToList(); + + foreach (var r in removable) + r.Remove = RemoveStatus.Removed; + + productsGrid_RemovableCountChanged(null); + } + catch (Exception ex) + { + MessageBoxLib.ShowAdminAlert( + this, + "Error scanning library. You may still manually select books to remove from Libation's library.", + "Error scanning library", + ex); + } + } + public void Display() { try @@ -125,5 +182,10 @@ namespace LibationWinForms.GridView { LiberateClicked?.Invoke(this, liveGridEntry.LibraryBook); } + + private void productsGrid_RemovableCountChanged(GridEntry liveGridEntry) + { + RemovableCountChanged?.Invoke(this, productsGrid.GetAllBookEntries().Count(lbe => lbe.Remove is RemoveStatus.Removed)); + } } } diff --git a/Source/LibationWinForms/GridView/ProductsGrid.Designer.cs b/Source/LibationWinForms/GridView/ProductsGrid.Designer.cs index b380ff95..bca1dec0 100644 --- a/Source/LibationWinForms/GridView/ProductsGrid.Designer.cs +++ b/Source/LibationWinForms/GridView/ProductsGrid.Designer.cs @@ -29,8 +29,9 @@ private void InitializeComponent() { this.components = new System.ComponentModel.Container(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); this.gridEntryDataGridView = new System.Windows.Forms.DataGridView(); + this.removeGVColumn = new System.Windows.Forms.DataGridViewCheckBoxColumn(); this.liberateGVColumn = new LibationWinForms.GridView.LiberateDataGridViewImageButtonColumn(); this.coverGVColumn = new System.Windows.Forms.DataGridViewImageColumn(); this.titleGVColumn = new System.Windows.Forms.DataGridViewTextBoxColumn(); @@ -60,41 +61,56 @@ 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.removeGVColumn, + 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; - 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; + 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.Dock = System.Windows.Forms.DockStyle.Fill; this.gridEntryDataGridView.Location = new System.Drawing.Point(0, 0); 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.Size = new System.Drawing.Size(1570, 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); // + // removeGVColumn + // + this.removeGVColumn.DataPropertyName = "Remove"; + this.removeGVColumn.FalseValue = ""; + this.removeGVColumn.Frozen = true; + this.removeGVColumn.HeaderText = "Remove"; + this.removeGVColumn.IndeterminateValue = ""; + this.removeGVColumn.MinimumWidth = 60; + this.removeGVColumn.Name = "removeGVColumn"; + this.removeGVColumn.Resizable = System.Windows.Forms.DataGridViewTriState.False; + this.removeGVColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic; + this.removeGVColumn.ThreeState = true; + this.removeGVColumn.TrueValue = ""; + this.removeGVColumn.Width = 60; + // // liberateGVColumn // this.liberateGVColumn.DataPropertyName = "Liberate"; @@ -223,7 +239,7 @@ this.AutoScroll = true; this.Controls.Add(this.gridEntryDataGridView); this.Name = "ProductsGrid"; - this.Size = new System.Drawing.Size(1510, 380); + this.Size = new System.Drawing.Size(1570, 380); this.Load += new System.EventHandler(this.ProductsGrid_Load); ((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).EndInit(); ((System.ComponentModel.ISupportInitialize)(this.syncBindingSource)).EndInit(); @@ -235,6 +251,8 @@ #endregion private System.Windows.Forms.DataGridView gridEntryDataGridView; private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; + private SyncBindingSource syncBindingSource; + private System.Windows.Forms.DataGridViewCheckBoxColumn removeGVColumn; private LiberateDataGridViewImageButtonColumn liberateGVColumn; private System.Windows.Forms.DataGridViewImageColumn coverGVColumn; private System.Windows.Forms.DataGridViewTextBoxColumn titleGVColumn; @@ -249,6 +267,5 @@ private System.Windows.Forms.DataGridViewTextBoxColumn myRatingGVColumn; private System.Windows.Forms.DataGridViewTextBoxColumn miscGVColumn; private EditTagsDataGridViewImageButtonColumn tagAndDetailsGVColumn; - private SyncBindingSource syncBindingSource; } } diff --git a/Source/LibationWinForms/GridView/ProductsGrid.cs b/Source/LibationWinForms/GridView/ProductsGrid.cs index c13a2d12..77a6d295 100644 --- a/Source/LibationWinForms/GridView/ProductsGrid.cs +++ b/Source/LibationWinForms/GridView/ProductsGrid.cs @@ -23,12 +23,15 @@ namespace LibationWinForms.GridView public event LibraryBookEntryClickedEventHandler DetailsClicked; public event GridEntryRectangleClickedEventHandler DescriptionClicked; public new event EventHandler Scroll; + public event GridEntryClickedEventHandler RemovableCountChanged; private GridEntryBindingList bindingList; internal IEnumerable GetVisibleBooks() => bindingList .BookEntries() .Select(lbe => lbe.LibraryBook); + internal IEnumerable GetAllBookEntries() + => bindingList.AllItems().BookEntries(); public ProductsGrid() { @@ -81,6 +84,12 @@ namespace LibationWinForms.GridView else if (e.ColumnIndex == coverGVColumn.Index) CoverClicked?.Invoke(sEntry); } + + if (e.ColumnIndex == removeGVColumn.Index) + { + gridEntryDataGridView.CommitEdit(DataGridViewDataErrorContexts.Commit); + RemovableCountChanged?.Invoke(entry); + } } private GridEntry getGridEntry(int rowIndex) => gridEntryDataGridView.GetBoundItem(rowIndex); @@ -89,6 +98,20 @@ namespace LibationWinForms.GridView #region UI display functions + internal bool RemoveColumnVisible + { + get => removeGVColumn.Visible; + set + { + if (value) + { + foreach (var book in bindingList.AllItems()) + book.Remove = RemoveStatus.NotRemoved; + } + removeGVColumn.Visible = value; + } + } + internal void BindToGrid(List dbBooks) { var geList = dbBooks.Where(lb => lb.Book.IsProduct()).Select(b => new LibraryBookEntry(b)).Cast().ToList(); @@ -153,6 +176,11 @@ namespace LibationWinForms.GridView .BookEntries() .ExceptBy(dbBooks.Select(lb => lb.Book.AudibleProductId), ge => ge.AudibleProductId); + RemoveBooks(removedBooks); + } + + public void RemoveBooks(IEnumerable removedBooks) + { //Remove books in series from their parents' Children list foreach (var removed in removedBooks.Where(b => b.Parent is not null)) { @@ -312,6 +340,14 @@ namespace LibationWinForms.GridView column.DisplayIndex = displayIndices.GetValueOrDefault(itemName, column.Index); } + + //Remove column is always first; + removeGVColumn.DisplayIndex = 0; + removeGVColumn.Visible = false; + removeGVColumn.ValueType = typeof(RemoveStatus); + removeGVColumn.FalseValue = RemoveStatus.NotRemoved; + removeGVColumn.TrueValue = RemoveStatus.Removed; + removeGVColumn.IndeterminateValue = RemoveStatus.SomeRemoved; } private void HideMenuItem_Click(object sender, EventArgs e) diff --git a/Source/LibationWinForms/GridView/ProductsGrid.resx b/Source/LibationWinForms/GridView/ProductsGrid.resx index bc15cd01..72deb6eb 100644 --- a/Source/LibationWinForms/GridView/ProductsGrid.resx +++ b/Source/LibationWinForms/GridView/ProductsGrid.resx @@ -57,16 +57,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + True + 171, 17 17, 17 - - 326, 17 - - - 326, 17 - \ No newline at end of file diff --git a/Source/LibationWinForms/GridView/SeriesEntry.cs b/Source/LibationWinForms/GridView/SeriesEntry.cs index ca422029..9ceba47e 100644 --- a/Source/LibationWinForms/GridView/SeriesEntry.cs +++ b/Source/LibationWinForms/GridView/SeriesEntry.cs @@ -13,7 +13,43 @@ namespace LibationWinForms.GridView [Browsable(false)] public List Children { get; } [Browsable(false)] public override DateTime DateAdded => Children.Max(c => c.DateAdded); + private bool suspendCounting = false; + public void ChildRemoveUpdate() + { + if (suspendCounting) return; + + var removeCount = Children.Count(c => c.Remove is RemoveStatus.Removed); + + if (removeCount == 0) + _remove = RemoveStatus.NotRemoved; + else if (removeCount == Children.Count) + _remove = RemoveStatus.Removed; + else + _remove = RemoveStatus.SomeRemoved; + NotifyPropertyChanged(nameof(Remove)); + } + #region Model properties exposed to the view + public override RemoveStatus Remove + { + get + { + return _remove; + } + set + { + _remove = value is RemoveStatus.SomeRemoved ? RemoveStatus.NotRemoved : value; + + suspendCounting = true; + + foreach (var item in Children) + item.Remove = value; + + suspendCounting = false; + + NotifyPropertyChanged(); + } + } public override LiberateButtonStatus Liberate { get; } public override string DisplayTags { get; } = string.Empty;