Refactor ProductsGrid
This commit is contained in:
parent
e8a320dac9
commit
593fe57ea1
33
Source/LibationWinForms/Form1.Designer.cs
generated
33
Source/LibationWinForms/Form1.Designer.cs
generated
@ -29,7 +29,6 @@
|
|||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
|
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.filterHelpBtn = new System.Windows.Forms.Button();
|
||||||
this.filterBtn = new System.Windows.Forms.Button();
|
this.filterBtn = new System.Windows.Forms.Button();
|
||||||
this.filterSearchTb = new System.Windows.Forms.TextBox();
|
this.filterSearchTb = new System.Windows.Forms.TextBox();
|
||||||
@ -73,6 +72,7 @@
|
|||||||
this.addQuickFilterBtn = new System.Windows.Forms.Button();
|
this.addQuickFilterBtn = new System.Windows.Forms.Button();
|
||||||
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
|
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
|
||||||
this.panel1 = new System.Windows.Forms.Panel();
|
this.panel1 = new System.Windows.Forms.Panel();
|
||||||
|
this.productsDisplay = new LibationWinForms.ProductsDisplay();
|
||||||
this.toggleQueueHideBtn = new System.Windows.Forms.Button();
|
this.toggleQueueHideBtn = new System.Windows.Forms.Button();
|
||||||
this.processBookQueue1 = new LibationWinForms.ProcessQueue.ProcessQueueControl();
|
this.processBookQueue1 = new LibationWinForms.ProcessQueue.ProcessQueueControl();
|
||||||
this.menuStrip1.SuspendLayout();
|
this.menuStrip1.SuspendLayout();
|
||||||
@ -84,17 +84,6 @@
|
|||||||
this.panel1.SuspendLayout();
|
this.panel1.SuspendLayout();
|
||||||
this.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.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
|
// filterHelpBtn
|
||||||
//
|
//
|
||||||
this.filterHelpBtn.Location = new System.Drawing.Point(15, 3);
|
this.filterHelpBtn.Location = new System.Drawing.Point(15, 3);
|
||||||
@ -471,8 +460,8 @@
|
|||||||
// panel1
|
// panel1
|
||||||
//
|
//
|
||||||
this.panel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
|
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.toggleQueueHideBtn);
|
||||||
this.panel1.Controls.Add(this.gridPanel);
|
|
||||||
this.panel1.Controls.Add(this.addQuickFilterBtn);
|
this.panel1.Controls.Add(this.addQuickFilterBtn);
|
||||||
this.panel1.Controls.Add(this.filterHelpBtn);
|
this.panel1.Controls.Add(this.filterHelpBtn);
|
||||||
this.panel1.Controls.Add(this.filterSearchTb);
|
this.panel1.Controls.Add(this.filterSearchTb);
|
||||||
@ -484,6 +473,21 @@
|
|||||||
this.panel1.Size = new System.Drawing.Size(893, 594);
|
this.panel1.Size = new System.Drawing.Size(893, 594);
|
||||||
this.panel1.TabIndex = 7;
|
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<DataLayer.LibraryBook>(this.ProductsDisplay_LiberateClicked);
|
||||||
|
this.productsDisplay.VisibleCountChanged += new System.EventHandler<int>(this.productsDisplay_VisibleCountChanged);
|
||||||
|
this.productsDisplay.InitialLoaded += new System.EventHandler(this.productsDisplay_InitialLoaded);
|
||||||
|
//
|
||||||
// toggleQueueHideBtn
|
// toggleQueueHideBtn
|
||||||
//
|
//
|
||||||
this.toggleQueueHideBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
this.toggleQueueHideBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
@ -534,8 +538,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private System.Windows.Forms.Panel gridPanel;
|
|
||||||
private System.Windows.Forms.MenuStrip menuStrip1;
|
private System.Windows.Forms.MenuStrip menuStrip1;
|
||||||
private System.Windows.Forms.ToolStripMenuItem importToolStripMenuItem;
|
private System.Windows.Forms.ToolStripMenuItem importToolStripMenuItem;
|
||||||
private System.Windows.Forms.StatusStrip statusStrip1;
|
private System.Windows.Forms.StatusStrip statusStrip1;
|
||||||
@ -581,5 +583,6 @@
|
|||||||
private LibationWinForms.ProcessQueue.ProcessQueueControl processBookQueue1;
|
private LibationWinForms.ProcessQueue.ProcessQueueControl processBookQueue1;
|
||||||
private System.Windows.Forms.Panel panel1;
|
private System.Windows.Forms.Panel panel1;
|
||||||
private System.Windows.Forms.Button toggleQueueHideBtn;
|
private System.Windows.Forms.Button toggleQueueHideBtn;
|
||||||
|
private ProductsDisplay productsDisplay;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,7 +30,7 @@ namespace LibationWinForms
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
productsGrid.Filter(filterString);
|
productsDisplay.Filter(filterString);
|
||||||
lastGoodFilter = filterString;
|
lastGoodFilter = filterString;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@ -14,14 +14,13 @@ namespace LibationWinForms
|
|||||||
int WidthChange = 0;
|
int WidthChange = 0;
|
||||||
private void Configure_ProcessQueue()
|
private void Configure_ProcessQueue()
|
||||||
{
|
{
|
||||||
productsGrid.LiberateClicked += ProductsGrid_LiberateClicked;
|
|
||||||
processBookQueue1.popoutBtn.Click += ProcessBookQueue1_PopOut;
|
processBookQueue1.popoutBtn.Click += ProcessBookQueue1_PopOut;
|
||||||
var coppalseState = Configuration.Instance.GetNonString<bool>(nameof(splitContainer1.Panel2Collapsed));
|
var coppalseState = Configuration.Instance.GetNonString<bool>(nameof(splitContainer1.Panel2Collapsed));
|
||||||
WidthChange = splitContainer1.Panel2.Width + splitContainer1.SplitterWidth;
|
WidthChange = splitContainer1.Panel2.Width + splitContainer1.SplitterWidth;
|
||||||
SetQueueCollapseState(coppalseState);
|
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)
|
if (e.Book.UserDefinedItem.BookStatus != LiberatedStatus.Liberated)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -14,12 +14,6 @@ namespace LibationWinForms
|
|||||||
Load += updateFiltersMenu;
|
Load += updateFiltersMenu;
|
||||||
QuickFilters.UseDefaultChanged += updateFirstFilterIsDefaultToolStripMenuItem;
|
QuickFilters.UseDefaultChanged += updateFirstFilterIsDefaultToolStripMenuItem;
|
||||||
QuickFilters.Updated += updateFiltersMenu;
|
QuickFilters.Updated += updateFiltersMenu;
|
||||||
|
|
||||||
productsGrid.InitialLoaded += (_, __) =>
|
|
||||||
{
|
|
||||||
if (QuickFilters.UseDefault)
|
|
||||||
performFilter(QuickFilters.Filters.FirstOrDefault());
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private object quickFilterTag { get; } = new();
|
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 addQuickFilterBtn_Click(object sender, EventArgs e) => QuickFilters.Add(this.filterSearchTb.Text);
|
||||||
|
|
||||||
private void editQuickFiltersToolStripMenuItem_Click(object sender, EventArgs e) => new EditQuickFilters().ShowDialog();
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,28 +17,16 @@ namespace LibationWinForms
|
|||||||
liberateVisibleToolStripMenuItem.Format(0);
|
liberateVisibleToolStripMenuItem.Format(0);
|
||||||
liberateVisible2ToolStripMenuItem.Format(0);
|
liberateVisible2ToolStripMenuItem.Format(0);
|
||||||
|
|
||||||
// bottom-left visible count
|
|
||||||
productsGrid.VisibleCountChanged += (_, qty) => visibleCountLbl.Format(qty);
|
|
||||||
|
|
||||||
// top menu strip
|
// top menu strip
|
||||||
visibleBooksToolStripMenuItem.Format(0);
|
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;
|
LibraryCommands.BookUserDefinedItemCommitted += setLiberatedVisibleMenuItemAsync;
|
||||||
}
|
}
|
||||||
private async void setLiberatedVisibleMenuItemAsync(object _, int __)
|
|
||||||
=> await Task.Run(setLiberatedVisibleMenuItem);
|
|
||||||
private async void setLiberatedVisibleMenuItemAsync(object _, EventArgs __)
|
private async void setLiberatedVisibleMenuItemAsync(object _, EventArgs __)
|
||||||
=> await Task.Run(setLiberatedVisibleMenuItem);
|
=> await Task.Run(setLiberatedVisibleMenuItem);
|
||||||
void 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(() =>
|
this.UIThreadSync(() =>
|
||||||
{
|
{
|
||||||
if (notLiberated > 0)
|
if (notLiberated > 0)
|
||||||
@ -63,7 +51,7 @@ namespace LibationWinForms
|
|||||||
private async void liberateVisible(object sender, EventArgs e)
|
private async void liberateVisible(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
SetQueueCollapseState(false);
|
SetQueueCollapseState(false);
|
||||||
await Task.Run(() => processBookQueue1.AddDownloadDecrypt(productsGrid.GetVisible()));
|
await Task.Run(() => processBookQueue1.AddDownloadDecrypt(productsDisplay.GetVisible()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void replaceTagsToolStripMenuItem_Click(object sender, EventArgs e)
|
private void replaceTagsToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
@ -73,7 +61,7 @@ namespace LibationWinForms
|
|||||||
if (result != DialogResult.OK)
|
if (result != DialogResult.OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var visibleLibraryBooks = productsGrid.GetVisible();
|
var visibleLibraryBooks = productsDisplay.GetVisible();
|
||||||
|
|
||||||
var confirmationResult = MessageBoxLib.ShowConfirmationDialog(
|
var confirmationResult = MessageBoxLib.ShowConfirmationDialog(
|
||||||
visibleLibraryBooks,
|
visibleLibraryBooks,
|
||||||
@ -95,7 +83,7 @@ namespace LibationWinForms
|
|||||||
if (result != DialogResult.OK)
|
if (result != DialogResult.OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var visibleLibraryBooks = productsGrid.GetVisible();
|
var visibleLibraryBooks = productsDisplay.GetVisible();
|
||||||
|
|
||||||
var confirmationResult = MessageBoxLib.ShowConfirmationDialog(
|
var confirmationResult = MessageBoxLib.ShowConfirmationDialog(
|
||||||
visibleLibraryBooks,
|
visibleLibraryBooks,
|
||||||
@ -112,7 +100,7 @@ namespace LibationWinForms
|
|||||||
|
|
||||||
private async void removeToolStripMenuItem_Click(object sender, EventArgs e)
|
private async void removeToolStripMenuItem_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var visibleLibraryBooks = productsGrid.GetVisible();
|
var visibleLibraryBooks = productsDisplay.GetVisible();
|
||||||
|
|
||||||
var confirmationResult = MessageBoxLib.ShowConfirmationDialog(
|
var confirmationResult = MessageBoxLib.ShowConfirmationDialog(
|
||||||
visibleLibraryBooks,
|
visibleLibraryBooks,
|
||||||
@ -125,5 +113,20 @@ namespace LibationWinForms
|
|||||||
var visibleIds = visibleLibraryBooks.Select(lb => lb.Book.AudibleProductId).ToList();
|
var visibleIds = visibleLibraryBooks.Select(lb => lb.Book.AudibleProductId).ToList();
|
||||||
await LibraryCommands.RemoveBooksAsync(visibleIds);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,24 +12,10 @@ namespace LibationWinForms
|
|||||||
{
|
{
|
||||||
public partial class Form1 : Form
|
public partial class Form1 : Form
|
||||||
{
|
{
|
||||||
private ProductsDisplay productsGrid { get; }
|
|
||||||
|
|
||||||
public Form1()
|
public Form1()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
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:
|
// 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
|
// 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();
|
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'
|
// 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();
|
this.Load += (_, __) => productsDisplay.Display();
|
||||||
LibraryCommands.LibrarySizeChanged += (_, __) => this.UIThreadAsync(() => productsGrid.Display());
|
LibraryCommands.LibrarySizeChanged += (_, __) => this.UIThreadAsync(() => productsDisplay.Display());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -45,9 +45,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Update="grid\ProductsGrid.cs">
|
|
||||||
<SubType>UserControl</SubType>
|
|
||||||
</Compile>
|
|
||||||
<Compile Update="Properties\Resources.Designer.cs">
|
<Compile Update="Properties\Resources.Designer.cs">
|
||||||
<DesignTime>True</DesignTime>
|
<DesignTime>True</DesignTime>
|
||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
@ -56,9 +53,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Update="grid\ProductsGrid.resx">
|
|
||||||
<SubType>Designer</SubType>
|
|
||||||
</EmbeddedResource>
|
|
||||||
<EmbeddedResource Update="Properties\Resources.resx">
|
<EmbeddedResource Update="Properties\Resources.resx">
|
||||||
<Generator>ResXFileCodeGenerator</Generator>
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System.Drawing;
|
using Dinah.Core.Windows.Forms;
|
||||||
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
|
|
||||||
namespace LibationWinForms
|
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)
|
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<GridEntry>(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 tagsString = (string)value;
|
||||||
|
|
||||||
var foreColor = tagsString?.Contains("hidden") == true ? HiddenForeColor : DataGridView.DefaultCellStyle.ForeColor;
|
var foreColor = tagsString?.Contains("hidden") == true ? HiddenForeColor : DataGridView.DefaultCellStyle.ForeColor;
|
||||||
|
|||||||
@ -50,14 +50,7 @@ namespace LibationWinForms
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <returns>All items in the list, including those filtered out.</returns>
|
/// <returns>All items in the list, including those filtered out.</returns>
|
||||||
public List<GridEntry> AllItems()
|
public List<GridEntry> AllItems() => Items.Concat(FilterRemoved).ToList();
|
||||||
{
|
|
||||||
var allItems = Items.Concat(FilterRemoved);
|
|
||||||
|
|
||||||
var series = allItems.Where(i => i is SeriesEntry).Cast<SeriesEntry>().SelectMany(s => s.Children);
|
|
||||||
|
|
||||||
return series.Concat(allItems).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ApplyFilter(string filterString)
|
private void ApplyFilter(string filterString)
|
||||||
{
|
{
|
||||||
@ -65,32 +58,41 @@ namespace LibationWinForms
|
|||||||
RemoveFilter();
|
RemoveFilter();
|
||||||
|
|
||||||
FilterString = filterString;
|
FilterString = filterString;
|
||||||
|
|
||||||
SearchResults = SearchEngineCommands.Search(filterString);
|
SearchResults = SearchEngineCommands.Search(filterString);
|
||||||
var filteredOut = Items.Where(i => i is LibraryBookEntry).Cast<LibraryBookEntry>().ExceptBy(SearchResults.Docs.Select(d => d.ProductId), ge => ge.AudibleProductId).Cast<GridEntry>().ToList();
|
|
||||||
|
|
||||||
var parents = Items.Where(i => i is SeriesEntry).Cast<SeriesEntry>();
|
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<LibraryBookEntry>().ExceptBy(SearchResults.Docs.Select(d => d.ProductId), ge => ge.AudibleProductId).Count() == p.Children.Count)
|
FilterRemoved.Add(item);
|
||||||
{
|
base.Remove(item);
|
||||||
//Don't show series whose episodes have all been filtered out
|
|
||||||
filteredOut.Add(p);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < filteredOut.Count; i++)
|
public void CollapseAll()
|
||||||
{
|
{
|
||||||
FilterRemoved.Add(filteredOut[i]);
|
foreach (var series in Items.Series().ToList())
|
||||||
base.Remove(filteredOut[i]);
|
CollapseItem(series);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ExpandAll()
|
||||||
|
{
|
||||||
|
foreach (var series in Items.Series().ToList())
|
||||||
|
ExpandItem(series);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CollapseItem(SeriesEntry sEntry)
|
public void CollapseItem(SeriesEntry sEntry)
|
||||||
{
|
{
|
||||||
foreach (var item in Items.Where(b => b is LibraryBookEntry).Cast<LibraryBookEntry>().Where(b => b.Parent == sEntry).ToList())
|
foreach (var episode in Items.Where(b => b.Parent == sEntry).Cast<LibraryBookEntry>().ToList())
|
||||||
base.Remove(item);
|
{
|
||||||
|
FilterRemoved.Add(episode);
|
||||||
|
base.Remove(episode);
|
||||||
|
}
|
||||||
|
|
||||||
sEntry.Liberate.Expanded = false;
|
sEntry.Liberate.Expanded = false;
|
||||||
}
|
}
|
||||||
@ -98,16 +100,17 @@ namespace LibationWinForms
|
|||||||
public void ExpandItem(SeriesEntry sEntry)
|
public void ExpandItem(SeriesEntry sEntry)
|
||||||
{
|
{
|
||||||
var sindex = Items.IndexOf(sEntry);
|
var sindex = Items.IndexOf(sEntry);
|
||||||
var children = sEntry.Children.Cast<LibraryBookEntry>().ToList();
|
|
||||||
for (int i = 0; i < children.Count; i++)
|
foreach (var episode in FilterRemoved.Where(b => b.Parent == sEntry).Cast<LibraryBookEntry>().ToList())
|
||||||
{
|
{
|
||||||
if (SearchResults is null || SearchResults.Docs.Any(d=> d.ProductId == children[i].AudibleProductId))
|
if (SearchResults is null || SearchResults.Docs.Any(d => d.ProductId == episode.AudibleProductId))
|
||||||
Insert(++sindex, children[i]);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
FilterRemoved.Add(children[i]);
|
FilterRemoved.Remove(episode);
|
||||||
|
Items.Insert(++sindex, episode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
|
||||||
sEntry.Liberate.Expanded = true;
|
sEntry.Liberate.Expanded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,13 +119,15 @@ namespace LibationWinForms
|
|||||||
if (FilterString is null) return;
|
if (FilterString is null) return;
|
||||||
|
|
||||||
int visibleCount = Items.Count;
|
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)
|
if (IsSortedCore)
|
||||||
Sort();
|
Sort();
|
||||||
|
|||||||
@ -5,25 +5,37 @@ using LibationFileManager;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;using System.Drawing;
|
||||||
using System.Drawing;
|
using System.Linq;
|
||||||
|
|
||||||
namespace LibationWinForms
|
namespace LibationWinForms
|
||||||
{
|
{
|
||||||
public interface IHierarchical<T> where T : class
|
public interface IHierarchical<T> where T : class
|
||||||
{
|
{
|
||||||
T Parent { get; }
|
T Parent { get; }
|
||||||
List<T> Children { get; }
|
|
||||||
}
|
}
|
||||||
internal class LiberateStatus
|
|
||||||
|
public class LiberateStatus : IComparable
|
||||||
{
|
{
|
||||||
public LiberatedStatus BookStatus;
|
public LiberatedStatus BookStatus;
|
||||||
public LiberatedStatus? PdfStatus;
|
public LiberatedStatus? PdfStatus;
|
||||||
public bool IsSeries;
|
public bool IsSeries;
|
||||||
public bool Expanded;
|
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<GridEntry>
|
public abstract class GridEntry : AsyncNotifyPropertyChanged, IMemberComparable, IHierarchical<GridEntry>
|
||||||
{
|
{
|
||||||
protected abstract Book Book { get; }
|
protected abstract Book Book { get; }
|
||||||
|
|
||||||
@ -38,11 +50,15 @@ namespace LibationWinForms
|
|||||||
NotifyPropertyChanged();
|
NotifyPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Browsable(false)]
|
||||||
|
public new bool InvokeRequired => base.InvokeRequired;
|
||||||
|
[Browsable(false)]
|
||||||
public GridEntry Parent { get; set; }
|
public GridEntry Parent { get; set; }
|
||||||
public List<GridEntry> Children { get; set; }
|
[Browsable(false)]
|
||||||
|
public abstract DateTime DateAdded { get; }
|
||||||
public abstract string ProductRating { get; protected set; }
|
public abstract string ProductRating { get; protected set; }
|
||||||
public abstract string PurchaseDate { 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 MyRating { get; protected set; }
|
||||||
public abstract string Series { get; protected set; }
|
public abstract string Series { get; protected set; }
|
||||||
public abstract string Title { get; protected set; }
|
public abstract string Title { get; protected set; }
|
||||||
@ -89,7 +105,7 @@ namespace LibationWinForms
|
|||||||
{ typeof(float), new ObjectComparer<float>() },
|
{ typeof(float), new ObjectComparer<float>() },
|
||||||
{ typeof(bool), new ObjectComparer<bool>() },
|
{ typeof(bool), new ObjectComparer<bool>() },
|
||||||
{ typeof(DateTime), new ObjectComparer<DateTime>() },
|
{ typeof(DateTime), new ObjectComparer<DateTime>() },
|
||||||
{ typeof(LiberatedStatus), new ObjectComparer<LiberatedStatus>() },
|
{ typeof(LiberateStatus), new ObjectComparer<LiberateStatus>() },
|
||||||
};
|
};
|
||||||
|
|
||||||
~GridEntry()
|
~GridEntry()
|
||||||
@ -97,4 +113,12 @@ namespace LibationWinForms
|
|||||||
PictureStorage.PictureCached -= PictureStorage_PictureCached;
|
PictureStorage.PictureCached -= PictureStorage_PictureCached;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static class GridEntryExtensions
|
||||||
|
{
|
||||||
|
public static IEnumerable<SeriesEntry> Series(this IEnumerable<GridEntry> gridEntries)
|
||||||
|
=> gridEntries.Where(i => i is SeriesEntry).Cast<SeriesEntry>();
|
||||||
|
public static IEnumerable<LibraryBookEntry> LibraryBooks(this IEnumerable<GridEntry> gridEntries)
|
||||||
|
=> gridEntries.Where(i => i is LibraryBookEntry).Cast<LibraryBookEntry>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ namespace LibationWinForms
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The View Model for a LibraryBook
|
/// The View Model for a LibraryBook
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class LibraryBookEntry : GridEntry
|
public class LibraryBookEntry : GridEntry
|
||||||
{
|
{
|
||||||
#region implementation properties NOT exposed to the view
|
#region implementation properties NOT exposed to the view
|
||||||
// hide from public fields from Data Source GUI with [Browsable(false)]
|
// hide from public fields from Data Source GUI with [Browsable(false)]
|
||||||
@ -187,7 +187,7 @@ namespace LibationWinForms
|
|||||||
{ nameof(Category), () => Category },
|
{ nameof(Category), () => Category },
|
||||||
{ nameof(Misc), () => Misc },
|
{ nameof(Misc), () => Misc },
|
||||||
{ nameof(DisplayTags), () => DisplayTags },
|
{ nameof(DisplayTags), () => DisplayTags },
|
||||||
{ nameof(Liberate), () => Liberate.BookStatus },
|
{ nameof(Liberate), () => Liberate },
|
||||||
{ nameof(DateAdded), () => DateAdded },
|
{ nameof(DateAdded), () => DateAdded },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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<GridEntry>(rowIndex);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -28,19 +28,38 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
|
this.productsGrid = new LibationWinForms.grid.ProductsGrid();
|
||||||
this.SuspendLayout();
|
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<int>(this.productsGrid_VisibleCountChanged);
|
||||||
|
//
|
||||||
// ProductsDisplay
|
// ProductsDisplay
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.Controls.Add(this.productsGrid);
|
||||||
this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.Name = "ProductsDisplay";
|
this.Name = "ProductsDisplay";
|
||||||
this.Size = new System.Drawing.Size(1510, 380);
|
this.Size = new System.Drawing.Size(1510, 380);
|
||||||
|
this.Load += new System.EventHandler(this.ProductsDisplay_Load);
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
private grid.ProductsGrid productsGrid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,65 +6,35 @@ using System.Threading.Tasks;
|
|||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using Dinah.Core.Windows.Forms;
|
|
||||||
using FileLiberator;
|
using FileLiberator;
|
||||||
using LibationFileManager;
|
using LibationFileManager;
|
||||||
using LibationWinForms.Dialogs;
|
using LibationWinForms.Dialogs;
|
||||||
|
|
||||||
namespace LibationWinForms
|
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 partial class ProductsDisplay : UserControl
|
||||||
{
|
{
|
||||||
public event EventHandler<LibraryBook> LiberateClicked;
|
public event EventHandler<LibraryBook> LiberateClicked;
|
||||||
/// <summary>Number of visible rows has changed</summary>
|
/// <summary>Number of visible rows has changed</summary>
|
||||||
public event EventHandler<int> VisibleCountChanged;
|
public event EventHandler<int> VisibleCountChanged;
|
||||||
|
|
||||||
// alias
|
|
||||||
|
|
||||||
private ProductsGrid grid;
|
|
||||||
|
|
||||||
public ProductsDisplay()
|
public ProductsDisplay()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
grid = new ProductsGrid();
|
private void ProductsDisplay_Load(object sender, EventArgs e)
|
||||||
grid.Dock = DockStyle.Fill;
|
{
|
||||||
Controls.Add(grid);
|
if (DesignMode)
|
||||||
|
|
||||||
if (this.DesignMode)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
grid.LiberateClicked += (_, book) => LiberateClicked?.Invoke(this, book.LibraryBook);
|
|
||||||
grid.DetailsClicked += Grid_DetailsClicked;
|
|
||||||
grid.CoverClicked += Grid_CoverClicked;
|
|
||||||
grid.DescriptionClicked += Grid_DescriptionClicked1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#region Button controls
|
#region Button controls
|
||||||
|
|
||||||
private ImageDisplay imageDisplay;
|
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 picDefinition = new PictureDefinition(liveGridEntry.LibraryBook.Book.PictureLarge ?? liveGridEntry.LibraryBook.Book.PictureId, PictureSize.Native);
|
||||||
var picDlTask = Task.Run(() => PictureStorage.GetPictureSynchronously(picDefinition));
|
var picDlTask = Task.Run(() => PictureStorage.GetPictureSynchronously(picDefinition));
|
||||||
@ -87,7 +57,7 @@ namespace LibationWinForms
|
|||||||
imageDisplay.CoverPicture = await picDlTask;
|
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
|
var displayWindow = new DescriptionDisplay
|
||||||
{
|
{
|
||||||
@ -101,13 +71,13 @@ namespace LibationWinForms
|
|||||||
displayWindow.Close();
|
displayWindow.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
grid.Scroll += CloseWindow;
|
productsGrid.Scroll += CloseWindow;
|
||||||
displayWindow.FormClosed += (_, _) => grid.Scroll -= CloseWindow;
|
displayWindow.FormClosed += (_, _) => productsGrid.Scroll -= CloseWindow;
|
||||||
displayWindow.Show(this);
|
displayWindow.Show(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void Grid_DetailsClicked(DataGridViewCellEventArgs e, LibraryBookEntry liveGridEntry)
|
private void productsGrid_DetailsClicked(LibraryBookEntry liveGridEntry)
|
||||||
{
|
{
|
||||||
var bookDetailsForm = new BookDetailsDialog(liveGridEntry.LibraryBook);
|
var bookDetailsForm = new BookDetailsDialog(liveGridEntry.LibraryBook);
|
||||||
if (bookDetailsForm.ShowDialog() == DialogResult.OK)
|
if (bookDetailsForm.ShowDialog() == DialogResult.OK)
|
||||||
@ -128,13 +98,12 @@ namespace LibationWinForms
|
|||||||
if (!hasBeenDisplayed)
|
if (!hasBeenDisplayed)
|
||||||
{
|
{
|
||||||
// bind
|
// bind
|
||||||
grid.bindToGrid(lib);
|
productsGrid.BindToGrid(lib);
|
||||||
hasBeenDisplayed = true;
|
hasBeenDisplayed = true;
|
||||||
InitialLoaded?.Invoke(this, new());
|
InitialLoaded?.Invoke(this, new());
|
||||||
VisibleCountChanged?.Invoke(this, grid.GetVisible().Count());
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
grid.updateGrid(lib);
|
productsGrid.UpdateGrid(lib);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,10 +112,20 @@ namespace LibationWinForms
|
|||||||
#region Filter
|
#region Filter
|
||||||
|
|
||||||
public void Filter(string searchString)
|
public void Filter(string searchString)
|
||||||
=> grid.Filter(searchString);
|
=> productsGrid.Filter(searchString);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
internal List<LibraryBook> GetVisible() => grid.GetVisible().ToList();
|
internal List<LibraryBook> 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
namespace LibationWinForms
|
namespace LibationWinForms.grid
|
||||||
{
|
{
|
||||||
partial class ProductsGrid
|
partial class ProductsGrid
|
||||||
{
|
{
|
||||||
@ -30,7 +30,6 @@
|
|||||||
{
|
{
|
||||||
this.components = new System.ComponentModel.Container();
|
this.components = new System.ComponentModel.Container();
|
||||||
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
|
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.gridEntryDataGridView = new System.Windows.Forms.DataGridView();
|
||||||
this.liberateGVColumn = new LibationWinForms.LiberateDataGridViewImageButtonColumn();
|
this.liberateGVColumn = new LibationWinForms.LiberateDataGridViewImageButtonColumn();
|
||||||
this.coverGVColumn = new System.Windows.Forms.DataGridViewImageColumn();
|
this.coverGVColumn = new System.Windows.Forms.DataGridViewImageColumn();
|
||||||
@ -47,21 +46,15 @@
|
|||||||
this.miscGVColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
this.miscGVColumn = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
this.tagAndDetailsGVColumn = new LibationWinForms.EditTagsDataGridViewImageButtonColumn();
|
this.tagAndDetailsGVColumn = new LibationWinForms.EditTagsDataGridViewImageButtonColumn();
|
||||||
this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
|
this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
|
||||||
((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).BeginInit();
|
|
||||||
((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).BeginInit();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// gridEntryBindingSource
|
|
||||||
//
|
|
||||||
this.gridEntryBindingSource.DataSource = typeof(LibationWinForms.GridEntry);
|
|
||||||
//
|
|
||||||
// gridEntryDataGridView
|
// gridEntryDataGridView
|
||||||
//
|
//
|
||||||
this.gridEntryDataGridView.AllowUserToAddRows = false;
|
this.gridEntryDataGridView.AllowUserToAddRows = false;
|
||||||
this.gridEntryDataGridView.AllowUserToDeleteRows = false;
|
this.gridEntryDataGridView.AllowUserToDeleteRows = false;
|
||||||
this.gridEntryDataGridView.AllowUserToOrderColumns = true;
|
this.gridEntryDataGridView.AllowUserToOrderColumns = true;
|
||||||
this.gridEntryDataGridView.AllowUserToResizeRows = false;
|
this.gridEntryDataGridView.AllowUserToResizeRows = false;
|
||||||
this.gridEntryDataGridView.AutoGenerateColumns = false;
|
|
||||||
this.gridEntryDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
this.gridEntryDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
||||||
this.gridEntryDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
|
this.gridEntryDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
|
||||||
this.liberateGVColumn,
|
this.liberateGVColumn,
|
||||||
@ -79,7 +72,6 @@
|
|||||||
this.miscGVColumn,
|
this.miscGVColumn,
|
||||||
this.tagAndDetailsGVColumn});
|
this.tagAndDetailsGVColumn});
|
||||||
this.gridEntryDataGridView.ContextMenuStrip = this.contextMenuStrip1;
|
this.gridEntryDataGridView.ContextMenuStrip = this.contextMenuStrip1;
|
||||||
this.gridEntryDataGridView.DataSource = this.gridEntryBindingSource;
|
|
||||||
dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
|
dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
|
||||||
dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Window;
|
dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Window;
|
||||||
dataGridViewCellStyle1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
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.DefaultCellStyle = dataGridViewCellStyle1;
|
||||||
this.gridEntryDataGridView.Dock = System.Windows.Forms.DockStyle.Fill;
|
this.gridEntryDataGridView.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
this.gridEntryDataGridView.Location = new System.Drawing.Point(0, 0);
|
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.Name = "gridEntryDataGridView";
|
||||||
this.gridEntryDataGridView.ReadOnly = true;
|
this.gridEntryDataGridView.ReadOnly = true;
|
||||||
this.gridEntryDataGridView.RowHeadersVisible = false;
|
this.gridEntryDataGridView.RowHeadersVisible = false;
|
||||||
this.gridEntryDataGridView.RowTemplate.Height = 82;
|
this.gridEntryDataGridView.RowTemplate.Height = 82;
|
||||||
this.gridEntryDataGridView.Size = new System.Drawing.Size(1510, 380);
|
this.gridEntryDataGridView.Size = new System.Drawing.Size(1510, 380);
|
||||||
this.gridEntryDataGridView.TabIndex = 0;
|
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.CellToolTipTextNeeded += new System.Windows.Forms.DataGridViewCellToolTipTextNeededEventHandler(this.gridEntryDataGridView_CellToolTipTextNeeded);
|
||||||
this.gridEntryDataGridView.ColumnDisplayIndexChanged += new System.Windows.Forms.DataGridViewColumnEventHandler(this.gridEntryDataGridView_ColumnDisplayIndexChanged);
|
this.gridEntryDataGridView.ColumnDisplayIndexChanged += new System.Windows.Forms.DataGridViewColumnEventHandler(this.gridEntryDataGridView_ColumnDisplayIndexChanged);
|
||||||
this.gridEntryDataGridView.ColumnWidthChanged += new System.Windows.Forms.DataGridViewColumnEventHandler(this.gridEntryDataGridView_ColumnWidthChanged);
|
|
||||||
//
|
//
|
||||||
// liberateGVColumn
|
// liberateGVColumn
|
||||||
//
|
//
|
||||||
@ -222,19 +213,18 @@
|
|||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
|
this.AutoScroll = true;
|
||||||
this.Controls.Add(this.gridEntryDataGridView);
|
this.Controls.Add(this.gridEntryDataGridView);
|
||||||
this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
|
||||||
this.Name = "ProductsGrid";
|
this.Name = "ProductsGrid";
|
||||||
this.Size = new System.Drawing.Size(1510, 380);
|
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();
|
((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).EndInit();
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private LibationWinForms.SyncBindingSource gridEntryBindingSource;
|
#endregion
|
||||||
private System.Windows.Forms.DataGridView gridEntryDataGridView;
|
private System.Windows.Forms.DataGridView gridEntryDataGridView;
|
||||||
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
|
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
|
||||||
private LiberateDataGridViewImageButtonColumn liberateGVColumn;
|
private LiberateDataGridViewImageButtonColumn liberateGVColumn;
|
||||||
|
|||||||
@ -1,103 +1,70 @@
|
|||||||
using System;
|
using DataLayer;
|
||||||
|
using Dinah.Core.Windows.Forms;
|
||||||
|
using LibationFileManager;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows.Forms;
|
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
|
public partial class ProductsGrid : UserControl
|
||||||
{
|
{
|
||||||
|
public delegate void LibraryBookEntryClickedEventHandler(LibraryBookEntry liveGridEntry);
|
||||||
internal delegate void LibraryBookEntryClickedEventHandler(DataGridViewCellEventArgs e, LibraryBookEntry liveGridEntry);
|
public delegate void LibraryBookEntryRectangleClickedEventHandler(LibraryBookEntry liveGridEntry, Rectangle cellRectangle);
|
||||||
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<ScrollEventArgs> Scroll;
|
|
||||||
|
|
||||||
private FilterableSortableBindingList bindingList;
|
|
||||||
|
|
||||||
/// <summary>Number of visible rows has changed</summary>
|
/// <summary>Number of visible rows has changed</summary>
|
||||||
public event EventHandler<int> VisibleCountChanged;
|
public event EventHandler<int> VisibleCountChanged;
|
||||||
|
public event LibraryBookEntryClickedEventHandler LiberateClicked;
|
||||||
|
public event LibraryBookEntryClickedEventHandler CoverClicked;
|
||||||
|
public event LibraryBookEntryClickedEventHandler DetailsClicked;
|
||||||
|
public event LibraryBookEntryRectangleClickedEventHandler DescriptionClicked;
|
||||||
|
public new event EventHandler<ScrollEventArgs> Scroll;
|
||||||
|
|
||||||
// alias
|
private FilterableSortableBindingList bindingList;
|
||||||
private DataGridView _dataGridView => gridEntryDataGridView;
|
private SyncBindingSource gridEntryBindingSource;
|
||||||
|
|
||||||
public ProductsGrid()
|
public ProductsGrid()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
if (this.DesignMode)
|
|
||||||
return;
|
|
||||||
|
|
||||||
EnableDoubleBuffering();
|
EnableDoubleBuffering();
|
||||||
|
//There a bug in designer that causes errors if you add BindingSource to the DataGridView at design time.
|
||||||
_dataGridView.CellContentClick += DataGridView_CellContentClick;
|
gridEntryBindingSource = new SyncBindingSource();
|
||||||
_dataGridView.Scroll += (_, s) => Scroll?.Invoke(this, s);
|
gridEntryDataGridView.DataSource = gridEntryBindingSource;
|
||||||
|
gridEntryDataGridView.Scroll += (_, s) => Scroll?.Invoke(this, s);
|
||||||
Load += ProductsGrid_Load;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProductsGrid_Scroll(object sender, ScrollEventArgs e)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnableDoubleBuffering()
|
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
|
#region Button controls
|
||||||
|
|
||||||
private void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
|
private void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
|
||||||
{
|
{
|
||||||
// handle grid button click: https://stackoverflow.com/a/13687844
|
// handle grid button click: https://stackoverflow.com/a/13687844
|
||||||
if ( e.RowIndex < 0)
|
if ( e.RowIndex < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var column = gridEntryDataGridView.Columns[e.ColumnIndex];
|
||||||
|
|
||||||
var entry = getGridEntry(e.RowIndex);
|
var entry = getGridEntry(e.RowIndex);
|
||||||
if (entry is LibraryBookEntry lbEntry)
|
if (entry is LibraryBookEntry lbEntry)
|
||||||
{
|
{
|
||||||
if (e.ColumnIndex == liberateGVColumn.Index)
|
if (gridEntryDataGridView.Columns[e.ColumnIndex].DataPropertyName == liberateGVColumn.DataPropertyName)
|
||||||
LiberateClicked?.Invoke(e, lbEntry);
|
LiberateClicked?.Invoke(lbEntry);
|
||||||
else if (e.ColumnIndex == tagAndDetailsGVColumn.Index && entry is LibraryBookEntry)
|
else if (gridEntryDataGridView.Columns[e.ColumnIndex].DataPropertyName == tagAndDetailsGVColumn.DataPropertyName && entry is LibraryBookEntry)
|
||||||
DetailsClicked?.Invoke(e, lbEntry);
|
DetailsClicked?.Invoke(lbEntry);
|
||||||
else if (e.ColumnIndex == descriptionGVColumn.Index)
|
else if (gridEntryDataGridView.Columns[e.ColumnIndex].DataPropertyName == descriptionGVColumn.DataPropertyName)
|
||||||
DescriptionClicked?.Invoke(e, lbEntry, _dataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false));
|
DescriptionClicked?.Invoke(lbEntry, gridEntryDataGridView.GetCellDisplayRectangle(e.ColumnIndex, e.RowIndex, false));
|
||||||
else if (e.ColumnIndex == coverGVColumn.Index)
|
else if (gridEntryDataGridView.Columns[e.ColumnIndex].DataPropertyName == coverGVColumn.DataPropertyName)
|
||||||
CoverClicked?.Invoke(e, lbEntry);
|
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)
|
if (sEntry.Liberate.Expanded)
|
||||||
bindingList.CollapseItem(sEntry);
|
bindingList.CollapseItem(sEntry);
|
||||||
@ -105,16 +72,19 @@ namespace LibationWinForms
|
|||||||
bindingList.ExpandItem(sEntry);
|
bindingList.ExpandItem(sEntry);
|
||||||
|
|
||||||
sEntry.NotifyPropertyChanged(nameof(sEntry.Liberate));
|
sEntry.NotifyPropertyChanged(nameof(sEntry.Liberate));
|
||||||
|
|
||||||
|
VisibleCountChanged?.Invoke(this, bindingList.LibraryBooks().Count());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private GridEntry getGridEntry(int rowIndex) => _dataGridView.GetBoundItem<GridEntry>(rowIndex);
|
private GridEntry getGridEntry(int rowIndex) => gridEntryDataGridView.GetBoundItem<GridEntry>(rowIndex);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
#region UI display functions
|
#region UI display functions
|
||||||
|
|
||||||
internal void bindToGrid(List<LibraryBook> dbBooks)
|
internal void BindToGrid(List<LibraryBook> dbBooks)
|
||||||
{
|
{
|
||||||
var geList = dbBooks.Where(b => b.Book.ContentType is not ContentType.Episode).Select(b => new LibraryBookEntry(b)).Cast<GridEntry>().ToList();
|
var geList = dbBooks.Where(b => b.Book.ContentType is not ContentType.Episode).Select(b => new LibraryBookEntry(b)).Cast<GridEntry>().ToList();
|
||||||
|
|
||||||
@ -125,24 +95,28 @@ namespace LibationWinForms
|
|||||||
foreach (var s in series)
|
foreach (var s in series)
|
||||||
{
|
{
|
||||||
var seriesEntry = new SeriesEntry();
|
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<GridEntry>().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);
|
seriesEntry.setSeriesBook(s);
|
||||||
|
|
||||||
geList.Add(seriesEntry);
|
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;
|
gridEntryBindingSource.DataSource = bindingList;
|
||||||
|
VisibleCountChanged?.Invoke(this, bindingList.LibraryBooks().Count());
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void updateGrid(List<LibraryBook> dbBooks)
|
internal void UpdateGrid(List<LibraryBook> dbBooks)
|
||||||
{
|
{
|
||||||
int visibleCount = bindingList.Count;
|
int visibleCount = bindingList.Count;
|
||||||
string existingFilter = gridEntryBindingSource.Filter;
|
string existingFilter = gridEntryBindingSource.Filter;
|
||||||
|
|
||||||
//Add absent books to grid, or update current books
|
//Add absent books to grid, or update current books
|
||||||
|
|
||||||
var allItmes = bindingList.AllItems().Where(i => i is LibraryBookEntry).Cast<LibraryBookEntry>();
|
var allItmes = bindingList.AllItems().LibraryBooks();
|
||||||
for (var i = dbBooks.Count - 1; i >= 0; i--)
|
for (var i = dbBooks.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var libraryBook = dbBooks[i];
|
var libraryBook = dbBooks[i];
|
||||||
@ -155,24 +129,29 @@ namespace LibationWinForms
|
|||||||
|
|
||||||
if (libraryBook.Book.ContentType is ContentType.Episode)
|
if (libraryBook.Book.ContentType is ContentType.Episode)
|
||||||
{
|
{
|
||||||
//Find the series that libraryBook, if it exists
|
//Find the series that libraryBook belongs to, if it exists
|
||||||
var series = bindingList.AllItems().Where(i => i is SeriesEntry).Cast<SeriesEntry>().FirstOrDefault(i => libraryBook.Book.SeriesLink.Any(s => s.Series.Name == i.Series));
|
var series = bindingList.AllItems().Series().FirstOrDefault(i => libraryBook.Book.SeriesLink.Any(s => s.Series.Name == i.Series));
|
||||||
|
|
||||||
if (series is null)
|
if (series is null)
|
||||||
{
|
{
|
||||||
//Series doesn't exist yet, so create and add it
|
//Series doesn't exist yet, so create and add it
|
||||||
var newSeries = new SeriesEntry { Children = new List<GridEntry> { lb } };
|
var newSeries = new SeriesEntry { Children = new List<LibraryBookEntry> { lb } };
|
||||||
newSeries.setSeriesBook(libraryBook.Book.SeriesLink.First());
|
newSeries.setSeriesBook(libraryBook.Book.SeriesLink.First());
|
||||||
lb.Parent = newSeries;
|
lb.Parent = newSeries;
|
||||||
newSeries.Liberate.Expanded = true;
|
newSeries.Liberate.Expanded = true;
|
||||||
bindingList.Insert(0, newSeries);
|
bindingList.Insert(0, newSeries);
|
||||||
|
series = newSeries;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lb.Parent = series;
|
lb.Parent = series;
|
||||||
series.Children.Add(lb);
|
series.Children.Add(lb);
|
||||||
}
|
}
|
||||||
|
//Add episode beneath the parent
|
||||||
|
int seriesIndex = bindingList.IndexOf(series);
|
||||||
|
bindingList.Insert(seriesIndex + 1, lb);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
//Add the new product
|
//Add the new product
|
||||||
bindingList.Insert(0, lb);
|
bindingList.Insert(0, lb);
|
||||||
}
|
}
|
||||||
@ -195,24 +174,27 @@ namespace LibationWinForms
|
|||||||
var removedBooks =
|
var removedBooks =
|
||||||
bindingList
|
bindingList
|
||||||
.AllItems()
|
.AllItems()
|
||||||
.Where(i => i is LibraryBookEntry)
|
.LibraryBooks()
|
||||||
.Cast<LibraryBookEntry>()
|
|
||||||
.ExceptBy(dbBooks.Select(lb => lb.Book.AudibleProductId), ge => ge.AudibleProductId);
|
.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
|
//Remove series that have no children
|
||||||
var removedSeries =
|
var removedSeries =
|
||||||
bindingList
|
bindingList
|
||||||
.AllItems()
|
.AllItems()
|
||||||
.Where(i => i is SeriesEntry)
|
.Series()
|
||||||
.Cast<SeriesEntry>()
|
.Where(i => i.Children.Count == 0);
|
||||||
.Where(i => removedBooks.Count(r => r.Series == i.Series) == i.Children.Count);
|
|
||||||
|
|
||||||
foreach (var removed in removedBooks.Cast<GridEntry>().Concat(removedSeries))
|
foreach (var removed in removedBooks.Cast<GridEntry>().Concat(removedSeries))
|
||||||
//no need to re-filter for removed books
|
//no need to re-filter for removed books
|
||||||
bindingList.Remove(removed);
|
bindingList.Remove(removed);
|
||||||
|
|
||||||
if (bindingList.Count != visibleCount)
|
if (bindingList.Count != visibleCount)
|
||||||
VisibleCountChanged?.Invoke(this, bindingList.Count);
|
VisibleCountChanged?.Invoke(this, bindingList.LibraryBooks().Count());
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -229,24 +211,19 @@ namespace LibationWinForms
|
|||||||
gridEntryBindingSource.Filter = searchString;
|
gridEntryBindingSource.Filter = searchString;
|
||||||
|
|
||||||
if (visibleCount != bindingList.Count)
|
if (visibleCount != bindingList.Count)
|
||||||
VisibleCountChanged?.Invoke(this, bindingList.Count);
|
VisibleCountChanged?.Invoke(this, bindingList.LibraryBooks().Count());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
internal IEnumerable<LibraryBook> GetVisible()
|
internal IEnumerable<LibraryBookEntry> GetVisible()
|
||||||
=> bindingList
|
=> bindingList
|
||||||
.Where(row => row is LibraryBookEntry)
|
.LibraryBooks();
|
||||||
.Cast<LibraryBookEntry>()
|
|
||||||
.Select(row => row.LibraryBook);
|
|
||||||
|
|
||||||
#region Column Customizations
|
|
||||||
|
|
||||||
// to ensure this is only ever called once: Load instead of 'override OnVisibleChanged'
|
|
||||||
private void ProductsGrid_Load(object sender, EventArgs e)
|
private void ProductsGrid_Load(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (this.DesignMode)
|
gridEntryDataGridView.ColumnWidthChanged += gridEntryDataGridView_ColumnWidthChanged;
|
||||||
return;
|
|
||||||
|
|
||||||
contextMenuStrip1.Items.Add(new ToolStripLabel("Show / Hide Columns"));
|
contextMenuStrip1.Items.Add(new ToolStripLabel("Show / Hide Columns"));
|
||||||
contextMenuStrip1.Items.Add(new ToolStripSeparator());
|
contextMenuStrip1.Items.Add(new ToolStripSeparator());
|
||||||
@ -259,7 +236,7 @@ namespace LibationWinForms
|
|||||||
|
|
||||||
var cmsKiller = new ContextMenuStrip();
|
var cmsKiller = new ContextMenuStrip();
|
||||||
|
|
||||||
foreach (DataGridViewColumn column in _dataGridView.Columns)
|
foreach (DataGridViewColumn column in gridEntryDataGridView.Columns)
|
||||||
{
|
{
|
||||||
var itemName = column.DataPropertyName;
|
var itemName = column.DataPropertyName;
|
||||||
var visible = gridColumnsVisibilities.GetValueOrDefault(itemName, true);
|
var visible = gridColumnsVisibilities.GetValueOrDefault(itemName, true);
|
||||||
@ -288,38 +265,19 @@ namespace LibationWinForms
|
|||||||
//We must set DisplayIndex properties in ascending order
|
//We must set DisplayIndex properties in ascending order
|
||||||
foreach (var itemName in displayIndices.OrderBy(i => i.Value).Select(i => i.Key))
|
foreach (var itemName in displayIndices.OrderBy(i => i.Value).Select(i => i.Key))
|
||||||
{
|
{
|
||||||
var column = _dataGridView.Columns
|
var column = gridEntryDataGridView.Columns
|
||||||
.Cast<DataGridViewColumn>()
|
.Cast<DataGridViewColumn>()
|
||||||
.Single(c => c.DataPropertyName == itemName);
|
.Single(c => c.DataPropertyName == itemName);
|
||||||
|
|
||||||
column.DisplayIndex = displayIndices.GetValueOrDefault(itemName, column.Index);
|
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)
|
private void HideMenuItem_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var menuItem = sender as ToolStripMenuItem;
|
var menuItem = sender as ToolStripMenuItem;
|
||||||
var propertyName = menuItem.Tag as string;
|
var propertyName = menuItem.Tag as string;
|
||||||
|
|
||||||
var column = _dataGridView.Columns
|
var column = gridEntryDataGridView.Columns
|
||||||
.Cast<DataGridViewColumn>()
|
.Cast<DataGridViewColumn>()
|
||||||
.FirstOrDefault(c => c.DataPropertyName == propertyName);
|
.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)
|
private void gridEntryDataGridView_CellToolTipTextNeeded(object sender, DataGridViewCellToolTipTextNeededEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.ColumnIndex == descriptionGVColumn.Index)
|
if (e.ColumnIndex == descriptionGVColumn.Index)
|
||||||
@ -345,6 +312,12 @@ namespace LibationWinForms
|
|||||||
e.ToolTipText = "Click to see full size";
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -57,7 +57,7 @@
|
|||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
<metadata name="contextMenuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
<value>81</value>
|
<value>17, 17</value>
|
||||||
</metadata>
|
</metadata>
|
||||||
</root>
|
</root>
|
||||||
@ -12,6 +12,7 @@ namespace LibationWinForms
|
|||||||
{
|
{
|
||||||
internal class SeriesEntry : GridEntry
|
internal class SeriesEntry : GridEntry
|
||||||
{
|
{
|
||||||
|
public List<LibraryBookEntry> Children { get; set; }
|
||||||
public override DateTime DateAdded => Children.Max(c => c.DateAdded);
|
public override DateTime DateAdded => Children.Max(c => c.DateAdded);
|
||||||
public override string ProductRating { get; protected set; }
|
public override string ProductRating { get; protected set; }
|
||||||
public override string PurchaseDate { 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 Category { get; protected set; }
|
||||||
public override string Misc { get; protected set; }
|
public override string Misc { get; protected set; }
|
||||||
public override string Description { 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;
|
public override LiberateStatus Liberate => _liberate;
|
||||||
|
|
||||||
@ -41,18 +42,17 @@ namespace LibationWinForms
|
|||||||
|
|
||||||
// Immutable properties
|
// Immutable properties
|
||||||
{
|
{
|
||||||
var childLB = Children.Cast<LibraryBookEntry>();
|
int bookLenMins = Children.Sum(c => c.LibraryBook.Book.LengthInMinutes);
|
||||||
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 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(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 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;
|
Title = SeriesBook.Series.Name;
|
||||||
Series = SeriesBook.Series.Name;
|
Series = SeriesBook.Series.Name;
|
||||||
Length = bookLenMins == 0 ? "" : $"{bookLenMins / 60} hr {bookLenMins % 60} min";
|
Length = bookLenMins == 0 ? "" : $"{bookLenMins / 60} hr {bookLenMins % 60} min";
|
||||||
MyRating = myAverageRating.ToStarString()?.DefaultIfNullOrWhiteSpace("");
|
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("");
|
ProductRating = productAverageRating.ToStarString()?.DefaultIfNullOrWhiteSpace("");
|
||||||
Authors = Book.AuthorNames();
|
Authors = Book.AuthorNames();
|
||||||
Narrators = Book.NarratorNames();
|
Narrators = Book.NarratorNames();
|
||||||
@ -73,17 +73,17 @@ namespace LibationWinForms
|
|||||||
{
|
{
|
||||||
{ nameof(Title), () => Book.SeriesSortable() },
|
{ nameof(Title), () => Book.SeriesSortable() },
|
||||||
{ nameof(Series), () => Book.SeriesSortable() },
|
{ nameof(Series), () => Book.SeriesSortable() },
|
||||||
{ nameof(Length), () => Children.Cast<LibraryBookEntry>().Sum(c=>c.LibraryBook.Book.LengthInMinutes) },
|
{ nameof(Length), () => Children.Sum(c=>c.LibraryBook.Book.LengthInMinutes) },
|
||||||
{ nameof(MyRating), () => Children.Cast<LibraryBookEntry>().Average(c=>c.LibraryBook.Book.UserDefinedItem.Rating.FirstScore()) },
|
{ nameof(MyRating), () => Children.Average(c=>c.LibraryBook.Book.UserDefinedItem.Rating.FirstScore()) },
|
||||||
{ nameof(PurchaseDate), () => Children.Cast<LibraryBookEntry>().Min(c=>c.LibraryBook.DateAdded) },
|
{ nameof(PurchaseDate), () => Children.Min(c=>c.LibraryBook.DateAdded) },
|
||||||
{ nameof(ProductRating), () => Children.Cast<LibraryBookEntry>().Average(c => c.LibraryBook.Book.Rating.FirstScore()) },
|
{ nameof(ProductRating), () => Children.Average(c => c.LibraryBook.Book.Rating.FirstScore()) },
|
||||||
{ nameof(Authors), () => string.Empty },
|
{ nameof(Authors), () => string.Empty },
|
||||||
{ nameof(Narrators), () => string.Empty },
|
{ nameof(Narrators), () => string.Empty },
|
||||||
{ nameof(Description), () => string.Empty },
|
{ nameof(Description), () => string.Empty },
|
||||||
{ nameof(Category), () => string.Empty },
|
{ nameof(Category), () => string.Empty },
|
||||||
{ nameof(Misc), () => string.Empty },
|
{ nameof(Misc), () => string.Empty },
|
||||||
{ nameof(DisplayTags), () => string.Empty },
|
{ nameof(DisplayTags), () => string.Empty },
|
||||||
{ nameof(Liberate), () => Liberate.BookStatus },
|
{ nameof(Liberate), () => Liberate },
|
||||||
{ nameof(DateAdded), () => DateAdded },
|
{ nameof(DateAdded), () => DateAdded },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,8 +3,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace LibationWinForms
|
namespace LibationWinForms
|
||||||
{
|
{
|
||||||
@ -42,22 +40,20 @@ namespace LibationWinForms
|
|||||||
{
|
{
|
||||||
List<T> itemsList = (List<T>)Items;
|
List<T> itemsList = (List<T>)Items;
|
||||||
|
|
||||||
//Array.Sort() and List<T>.Sort() are unstable sorts. OrderBy is stable.
|
var sortedItems = Items.OrderBy(ge => ge, Comparer).ToList();
|
||||||
var sortedItems = itemsList.OrderBy((ge) => ge, Comparer).ToList();
|
|
||||||
|
|
||||||
var children = sortedItems.Where(i => i.Parent is not null).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.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);
|
var pIndex = itemsList.IndexOf(parent);
|
||||||
foreach (var c in children.Where(c=> c.Parent == p))
|
foreach (var c in children.Where(c=> c.Parent == parent))
|
||||||
itemsList.Insert(++pIndex, c);
|
itemsList.Insert(++pIndex, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,26 +82,5 @@ namespace LibationWinForms
|
|||||||
|
|
||||||
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user