diff --git a/LibationWinForms/Dialogs/RemoveBooksDialog.Designer.cs b/LibationWinForms/Dialogs/RemoveBooksDialog.Designer.cs
new file mode 100644
index 00000000..d4484ce3
--- /dev/null
+++ b/LibationWinForms/Dialogs/RemoveBooksDialog.Designer.cs
@@ -0,0 +1,188 @@
+
+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.dataGridView1 = new System.Windows.Forms.DataGridView();
+ this.gridEntryBindingSource = new System.Windows.Forms.BindingSource(this.components);
+ this.btnRemoveBooks = new System.Windows.Forms.Button();
+ this.label1 = new System.Windows.Forms.Label();
+ 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.DatePurchased = new System.Windows.Forms.DataGridViewTextBoxColumn();
+ ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).BeginInit();
+ this.SuspendLayout();
+ //
+ // dataGridView1
+ //
+ this.dataGridView1.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.dataGridView1.AutoGenerateColumns = false;
+ this.dataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
+ this.dataGridView1.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
+ this.removeDataGridViewCheckBoxColumn,
+ this.coverDataGridViewImageColumn,
+ this.titleDataGridViewTextBoxColumn,
+ this.authorsDataGridViewTextBoxColumn,
+ this.miscDataGridViewTextBoxColumn,
+ this.DatePurchased});
+ this.dataGridView1.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.dataGridView1.DefaultCellStyle = dataGridViewCellStyle1;
+ this.dataGridView1.Location = new System.Drawing.Point(0, 0);
+ this.dataGridView1.Name = "dataGridView1";
+ this.dataGridView1.RowHeadersVisible = false;
+ this.dataGridView1.RowTemplate.Height = 82;
+ this.dataGridView1.Size = new System.Drawing.Size(800, 409);
+ this.dataGridView1.TabIndex = 0;
+ //
+ // 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(570, 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(169, 15);
+ this.label1.TabIndex = 2;
+ this.label1.Text = "{0} books selected for removal.";
+ //
+ // removeDataGridViewCheckBoxColumn
+ //
+ this.removeDataGridViewCheckBoxColumn.DataPropertyName = "Remove";
+ this.removeDataGridViewCheckBoxColumn.FalseValue = "False";
+ this.removeDataGridViewCheckBoxColumn.Frozen = true;
+ this.removeDataGridViewCheckBoxColumn.HeaderText = "Remove";
+ this.removeDataGridViewCheckBoxColumn.MinimumWidth = 60;
+ this.removeDataGridViewCheckBoxColumn.Name = "removeDataGridViewCheckBoxColumn";
+ this.removeDataGridViewCheckBoxColumn.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
+ this.removeDataGridViewCheckBoxColumn.TrueValue = "True";
+ this.removeDataGridViewCheckBoxColumn.Width = 60;
+ //
+ // 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;
+ //
+ // DatePurchased
+ //
+ this.DatePurchased.DataPropertyName = "DatePurchased";
+ this.DatePurchased.HeaderText = "Date Purchased";
+ this.DatePurchased.Name = "DatePurchased";
+ this.DatePurchased.ReadOnly = true;
+ this.DatePurchased.Width = 120;
+ //
+ // RemoveBooksDialog
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(800, 450);
+ this.Controls.Add(this.label1);
+ this.Controls.Add(this.btnRemoveBooks);
+ this.Controls.Add(this.dataGridView1);
+ this.Name = "RemoveBooksDialog";
+ this.Text = "RemoveBooksDialog";
+ this.Shown += new System.EventHandler(this.RemoveBooksDialog_Shown);
+ ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.DataGridView dataGridView1;
+ private System.Windows.Forms.BindingSource 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 DatePurchased;
+ }
+}
\ No newline at end of file
diff --git a/LibationWinForms/Dialogs/RemoveBooksDialog.cs b/LibationWinForms/Dialogs/RemoveBooksDialog.cs
new file mode 100644
index 00000000..273176cb
--- /dev/null
+++ b/LibationWinForms/Dialogs/RemoveBooksDialog.cs
@@ -0,0 +1,217 @@
+using ApplicationServices;
+using DataLayer;
+using InternalUtilities;
+using LibationWinForms.Login;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Windows.Forms;
+using Dinah.Core.DataBinding;
+using System.Runtime.CompilerServices;
+using Dinah.Core.Drawing;
+using System.Collections;
+
+namespace LibationWinForms.Dialogs
+{
+ public partial class RemoveBooksDialog : Form
+ {
+ public bool BooksRemoved { get; private set; }
+
+ private Account[] _accounts { get; }
+ private List _libraryBooks;
+ private SortableBindingList _removableGridEntries;
+ private string _labelFormat;
+ private int SelectedCount => SelectedEntries?.Count() ?? 0;
+ private IEnumerable SelectedEntries => _removableGridEntries?.Where(b => b.Remove);
+
+ public RemoveBooksDialog(params Account[] accounts)
+ {
+ _libraryBooks = DbContexts.GetContext().GetLibrary_Flat_NoTracking();
+ _accounts = accounts;
+ InitializeComponent();
+ _labelFormat = label1.Text;
+
+ dataGridView1.CellContentClick += (s, e) => dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit);
+ dataGridView1.CellValueChanged += DataGridView1_CellValueChanged;
+ dataGridView1.BindingContextChanged += (s, e) => UpdateSelection();
+
+ var orderedGridEntries = _libraryBooks
+ .Select(lb => new RemovableGridEntry(new GridEntry(lb)))
+ .OrderByDescending(ge => ge.GridEntry.Purchase_Date)
+ .ToList();
+
+ _removableGridEntries = orderedGridEntries.ToSortableBindingList();
+ gridEntryBindingSource.DataSource = _removableGridEntries;
+
+ dataGridView1.Enabled = false;
+ }
+
+ private void DataGridView1_CellValueChanged(object sender, DataGridViewCellEventArgs e)
+ {
+ if (e.ColumnIndex == 0)
+ {
+ UpdateSelection();
+ }
+ }
+
+ private async void RemoveBooksDialog_Shown(object sender, EventArgs e)
+ {
+ if (_accounts == null || _accounts.Length == 0)
+ return;
+ try
+ {
+ var rmovedBooks = await LibraryCommands.FindInactiveBooks((account) => new WinformResponder(account), _libraryBooks, _accounts);
+
+ var removable = _removableGridEntries.Where(rge => rmovedBooks.Count(rb => rb.Book.AudibleProductId == rge.GridEntry.AudibleProductId) == 1);
+
+ if (removable.Count() == 0)
+ return;
+
+ foreach (var r in removable)
+ r.Remove = true;
+
+ UpdateSelection();
+ }
+ catch (Exception ex)
+ {
+ MessageBoxAlertAdmin.Show(
+ "Error scanning library. You may still manually select books to remove from Libation's library.",
+ "Error scanning library",
+ ex);
+ }
+ finally
+ {
+ dataGridView1.Enabled = true;
+ }
+ }
+
+ private void btnRemoveBooks_Click(object sender, EventArgs e)
+ {
+ var selected = SelectedEntries.ToList();
+
+ if (selected.Count == 0) return;
+
+ string titles = string.Join("\r\n", selected.Select(rge => "-" + rge.Title));
+
+ var result = MessageBox.Show(
+ this,
+ $"Are you sure you want to remove the following {selected.Count} books from Libation's library?\r\n\r\n{titles}",
+ "Remove books from Libation?",
+ MessageBoxButtons.YesNo,
+ MessageBoxIcon.Question,
+ MessageBoxDefaultButton.Button1);
+
+ if (result == DialogResult.Yes)
+ {
+ using var context = DbContexts.GetContext();
+
+ var libBooks = context.GetLibrary_Flat_NoTracking();
+
+ var removeLibraryBooks = libBooks.Where(lb => selected.Count(rge => rge.GridEntry.AudibleProductId == lb.Book.AudibleProductId) == 1).ToArray();
+ context.Library.RemoveRange(removeLibraryBooks);
+ context.SaveChanges();
+ BooksRemoved = true;
+
+ foreach (var rEntry in selected)
+ _removableGridEntries.Remove(rEntry);
+
+ UpdateSelection();
+ }
+ }
+ private void UpdateSelection()
+ {
+ dataGridView1.Sort(dataGridView1.Columns[0], ListSortDirection.Descending);
+ var selectedCount = SelectedCount;
+ label1.Text = string.Format(_labelFormat, selectedCount);
+ btnRemoveBooks.Enabled = selectedCount > 0;
+ }
+ }
+ class CompareBool : IComparer
+ {
+ public int Compare(object x, object y)
+ {
+ var rge1 = x as RemovableGridEntry;
+ var rge2 = y as RemovableGridEntry;
+
+ return rge1.Remove.CompareTo(rge2.Remove);
+ }
+ }
+
+
+ internal class RemovableGridEntry : INotifyPropertyChanged
+ {
+ public event PropertyChangedEventHandler PropertyChanged;
+ public GridEntry GridEntry { get; }
+
+ public bool Remove
+ {
+ get
+ {
+ return _remove;
+ }
+ set
+ {
+ if (_remove != value)
+ {
+ _remove = value;
+ NotifyPropertyChanged();
+ }
+ }
+ }
+ public Image Cover
+ {
+ get
+ {
+ return _cover;
+ }
+ set
+ {
+ _cover = value;
+ NotifyPropertyChanged();
+ }
+ }
+ public string Title => GetDisplayValue(nameof(GridEntry.Title), GridEntry.Title);
+ public string Authors => GetDisplayValue(nameof(GridEntry.Authors), GridEntry.Authors);
+ public string Misc => GetDisplayValue(nameof(GridEntry.Misc), GridEntry.Misc);
+ public string DatePurchased => GetDisplayValue(nameof(GridEntry.Purchase_Date), GridEntry.Purchase_Date);
+
+ private bool _remove = false;
+ private Image _cover;
+
+ public RemovableGridEntry(GridEntry gridEntry)
+ {
+ GridEntry = gridEntry;
+
+ var picDef = new FileManager.PictureDefinition(GridEntry.GetBook().PictureId, FileManager.PictureSize._80x80);
+ (bool isDefault, byte[] picture) = FileManager.PictureStorage.GetPicture(picDef);
+
+ if (isDefault)
+ FileManager.PictureStorage.PictureCached += PictureStorage_PictureCached;
+
+ _cover = ImageReader.ToImage(picture);
+ }
+
+ private void PictureStorage_PictureCached(object sender, string pictureId)
+ {
+ if (pictureId == GridEntry.GetBook().PictureId)
+ {
+ Cover = WindowsDesktopUtilities.WinAudibleImageServer.GetImage(pictureId, FileManager.PictureSize._80x80);
+ FileManager.PictureStorage.PictureCached -= PictureStorage_PictureCached;
+ }
+ }
+
+ private string GetDisplayValue(string propertyName, string defaultValue)
+ {
+ if (GridEntry.TryDisplayValue(propertyName, out string value))
+ return value;
+ return defaultValue;
+ }
+
+ private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") =>
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+
+ }
+}
diff --git a/LibationWinForms/Dialogs/RemoveBooksDialog.resx b/LibationWinForms/Dialogs/RemoveBooksDialog.resx
new file mode 100644
index 00000000..303f91a6
--- /dev/null
+++ b/LibationWinForms/Dialogs/RemoveBooksDialog.resx
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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
+
+
+ True
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/LibationWinForms/Properties/DataSources/LibationWinForm.RemovableGridEntry.datasource b/LibationWinForms/Properties/DataSources/LibationWinForm.RemovableGridEntry.datasource
new file mode 100644
index 00000000..da01a0bf
--- /dev/null
+++ b/LibationWinForms/Properties/DataSources/LibationWinForm.RemovableGridEntry.datasource
@@ -0,0 +1,10 @@
+
+
+
+ LibationWinForms.Dialogs.RemovableGridEntry, LibationWinForms, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
+
\ No newline at end of file