Major UI refactoring.
This commit is contained in:
parent
af48641281
commit
d48bd5ad07
@ -13,7 +13,7 @@
|
|||||||
<!-- <PublishSingleFile>true</PublishSingleFile> -->
|
<!-- <PublishSingleFile>true</PublishSingleFile> -->
|
||||||
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
|
||||||
|
|
||||||
<Version>5.4.9.0</Version>
|
<Version>5.4.9.79</Version>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
50
LibationWinForms/DataGridViewImageButtonColumn.cs
Normal file
50
LibationWinForms/DataGridViewImageButtonColumn.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace LibationWinForms
|
||||||
|
{
|
||||||
|
public abstract class DataGridViewImageButtonColumn : DataGridViewButtonColumn
|
||||||
|
{
|
||||||
|
private DataGridViewImageButtonCell _cellTemplate;
|
||||||
|
public override DataGridViewCell CellTemplate
|
||||||
|
{
|
||||||
|
get => GetCellTemplate();
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value is DataGridViewImageButtonCell cellTemplate)
|
||||||
|
_cellTemplate = cellTemplate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract DataGridViewImageButtonCell NewCell();
|
||||||
|
|
||||||
|
private DataGridViewImageButtonCell GetCellTemplate()
|
||||||
|
{
|
||||||
|
if (_cellTemplate is null)
|
||||||
|
return NewCell();
|
||||||
|
else
|
||||||
|
return _cellTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object Clone()
|
||||||
|
{
|
||||||
|
var clone = (DataGridViewImageButtonColumn)base.Clone();
|
||||||
|
clone._cellTemplate = _cellTemplate;
|
||||||
|
|
||||||
|
return clone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DataGridViewImageButtonCell : DataGridViewButtonCell
|
||||||
|
{
|
||||||
|
protected void DrawImage(Graphics graphics, Bitmap image, Rectangle cellBounds)
|
||||||
|
{
|
||||||
|
var w = image.Width;
|
||||||
|
var h = image.Height;
|
||||||
|
var x = cellBounds.Left + (cellBounds.Width - w) / 2;
|
||||||
|
var y = cellBounds.Top + (cellBounds.Height - h) / 2;
|
||||||
|
|
||||||
|
graphics.DrawImage(image, new Rectangle(x, y, w, h));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -40,7 +40,7 @@ namespace LibationWinForms.Dialogs
|
|||||||
|
|
||||||
var orderedGridEntries = _libraryBooks
|
var orderedGridEntries = _libraryBooks
|
||||||
.Select(lb => new RemovableGridEntry(new GridEntry(lb)))
|
.Select(lb => new RemovableGridEntry(new GridEntry(lb)))
|
||||||
.OrderByDescending(ge => ge.GridEntry.Purchase_Date)
|
.OrderByDescending(ge => ge.GridEntry.PurchaseDate)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
_removableGridEntries = orderedGridEntries.ToSortableBindingList();
|
_removableGridEntries = orderedGridEntries.ToSortableBindingList();
|
||||||
@ -173,10 +173,10 @@ namespace LibationWinForms.Dialogs
|
|||||||
NotifyPropertyChanged();
|
NotifyPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public string Title => GetDisplayValue(nameof(GridEntry.Title), GridEntry.Title);
|
public string Title => GridEntry.Title;
|
||||||
public string Authors => GetDisplayValue(nameof(GridEntry.Authors), GridEntry.Authors);
|
public string Authors => GridEntry.Authors;
|
||||||
public string Misc => GetDisplayValue(nameof(GridEntry.Misc), GridEntry.Misc);
|
public string Misc => GridEntry.Misc;
|
||||||
public string DatePurchased => GetDisplayValue(nameof(GridEntry.Purchase_Date), GridEntry.Purchase_Date);
|
public string DatePurchased => GridEntry.PurchaseDate;
|
||||||
|
|
||||||
private bool _remove = false;
|
private bool _remove = false;
|
||||||
private Image _cover;
|
private Image _cover;
|
||||||
@ -185,7 +185,7 @@ namespace LibationWinForms.Dialogs
|
|||||||
{
|
{
|
||||||
GridEntry = gridEntry;
|
GridEntry = gridEntry;
|
||||||
|
|
||||||
var picDef = new FileManager.PictureDefinition(GridEntry.GetBook().PictureId, FileManager.PictureSize._80x80);
|
var picDef = new FileManager.PictureDefinition(GridEntry.LibraryBook.Book.PictureId, FileManager.PictureSize._80x80);
|
||||||
(bool isDefault, byte[] picture) = FileManager.PictureStorage.GetPicture(picDef);
|
(bool isDefault, byte[] picture) = FileManager.PictureStorage.GetPicture(picDef);
|
||||||
|
|
||||||
if (isDefault)
|
if (isDefault)
|
||||||
@ -196,20 +196,13 @@ namespace LibationWinForms.Dialogs
|
|||||||
|
|
||||||
private void PictureStorage_PictureCached(object sender, string pictureId)
|
private void PictureStorage_PictureCached(object sender, string pictureId)
|
||||||
{
|
{
|
||||||
if (pictureId == GridEntry.GetBook().PictureId)
|
if (pictureId == GridEntry.LibraryBook.Book.PictureId)
|
||||||
{
|
{
|
||||||
Cover = WindowsDesktopUtilities.WinAudibleImageServer.GetImage(pictureId, FileManager.PictureSize._80x80);
|
Cover = WindowsDesktopUtilities.WinAudibleImageServer.GetImage(pictureId, FileManager.PictureSize._80x80);
|
||||||
FileManager.PictureStorage.PictureCached -= PictureStorage_PictureCached;
|
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 = "") =>
|
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") =>
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
|
||||||
|
|||||||
26
LibationWinForms/EditTagsDataGridViewImageButtonColumn.cs
Normal file
26
LibationWinForms/EditTagsDataGridViewImageButtonColumn.cs
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace LibationWinForms
|
||||||
|
{
|
||||||
|
public class EditTagsDataGridViewImageButtonColumn : DataGridViewImageButtonColumn
|
||||||
|
{
|
||||||
|
protected override DataGridViewImageButtonCell NewCell()
|
||||||
|
=> new EditTagsDataGridViewImageButtonCell();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class EditTagsDataGridViewImageButtonCell : DataGridViewImageButtonCell
|
||||||
|
{
|
||||||
|
private static readonly Bitmap ButtonImage = Properties.Resources.edit_tags_25x25;
|
||||||
|
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 (((string)value).Length == 0)
|
||||||
|
{
|
||||||
|
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, paintParts);
|
||||||
|
DrawImage(graphics, ButtonImage, cellBounds);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,86 +1,159 @@
|
|||||||
using System;
|
using System;
|
||||||
|
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;
|
using System.Linq;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
|
using Dinah.Core.Drawing;
|
||||||
|
|
||||||
namespace LibationWinForms
|
namespace LibationWinForms
|
||||||
{
|
{
|
||||||
internal class GridEntry
|
internal class GridEntry : INotifyPropertyChanged, IObjectMemberComparable
|
||||||
{
|
{
|
||||||
private LibraryBook libraryBook { get; }
|
public const string LIBERATE_COLUMN_NAME = "Liberate";
|
||||||
private Book book => libraryBook.Book;
|
public const string EDIT_TAGS_COLUMN_NAME = "DisplayTags";
|
||||||
|
|
||||||
public Book GetBook() => book;
|
|
||||||
public LibraryBook GetLibraryBook() => libraryBook;
|
|
||||||
|
|
||||||
public GridEntry(LibraryBook libraryBook) => this.libraryBook = libraryBook;
|
|
||||||
|
|
||||||
|
#region implementation properties
|
||||||
// hide from public fields from Data Source GUI with [Browsable(false)]
|
// hide from public fields from Data Source GUI with [Browsable(false)]
|
||||||
|
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
public string AudibleProductId => book.AudibleProductId;
|
public string AudibleProductId => Book.AudibleProductId;
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
public string Tags => book.UserDefinedItem.Tags;
|
public string Tags => Book.UserDefinedItem.Tags;
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
public IEnumerable<string> TagsEnumerated => book.UserDefinedItem.TagsEnumerated;
|
public IEnumerable<string> TagsEnumerated => Book.UserDefinedItem.TagsEnumerated;
|
||||||
[Browsable(false)]
|
[Browsable(false)]
|
||||||
public string PictureId => book.PictureId;
|
public LibraryBook LibraryBook { get; }
|
||||||
[Browsable(false)]
|
|
||||||
public LiberatedState Liberated_Status => LibraryCommands.Liberated_Status(book);
|
|
||||||
[Browsable(false)]
|
|
||||||
public PdfState Pdf_Status => LibraryCommands.Pdf_Status(book);
|
|
||||||
|
|
||||||
// displayValues is what gets displayed
|
#endregion
|
||||||
// the value that gets returned from the property is the cell's value
|
|
||||||
// this allows for the value to be sorted one way and displayed another
|
|
||||||
// eg:
|
|
||||||
// orig title: The Computer
|
|
||||||
// formatReplacement: The Computer
|
|
||||||
// value for sorting: Computer
|
|
||||||
private Dictionary<string, string> displayValues { get; } = new Dictionary<string, string>();
|
|
||||||
public bool TryDisplayValue(string key, out string value) => displayValues.TryGetValue(key, out value);
|
|
||||||
|
|
||||||
public Image Cover =>
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
WindowsDesktopUtilities.WinAudibleImageServer.GetImage(book.PictureId, FileManager.PictureSize._80x80);
|
private Book Book => LibraryBook.Book;
|
||||||
|
private Image _cover;
|
||||||
|
|
||||||
public string Title
|
public GridEntry(LibraryBook libraryBook)
|
||||||
|
{
|
||||||
|
LibraryBook = libraryBook;
|
||||||
|
|
||||||
|
_compareValues = CreatePropertyValueDictionary();
|
||||||
|
|
||||||
|
//Get cover art. If it's default, subscribe to PictureCached
|
||||||
|
var picDef = new FileManager.PictureDefinition(Book.PictureId, FileManager.PictureSize._80x80);
|
||||||
|
(bool isDefault, byte[] picture) = FileManager.PictureStorage.GetPicture(picDef);
|
||||||
|
|
||||||
|
if (isDefault)
|
||||||
|
FileManager.PictureStorage.PictureCached += PictureStorage_PictureCached;
|
||||||
|
|
||||||
|
//Mutable property. Set the field so PropertyChanged doesn't fire.
|
||||||
|
_cover = ImageReader.ToImage(picture);
|
||||||
|
|
||||||
|
//Immutable properties
|
||||||
|
{
|
||||||
|
Title = Book.Title;
|
||||||
|
Series = Book.SeriesNames;
|
||||||
|
Length = Book.LengthInMinutes == 0 ? "" : $"{Book.LengthInMinutes / 60} hr {Book.LengthInMinutes % 60} min";
|
||||||
|
MyRating = GetStarString(Book.UserDefinedItem.Rating);
|
||||||
|
PurchaseDate = libraryBook.DateAdded.ToString("d");
|
||||||
|
ProductRating = GetStarString(Book.Rating);
|
||||||
|
Authors = Book.AuthorNames;
|
||||||
|
Narrators = Book.NarratorNames;
|
||||||
|
Category = string.Join(" > ", Book.CategoriesNames);
|
||||||
|
Misc = GetMiscDisplay(libraryBook);
|
||||||
|
Description = GetDescriptionDisplay(Book);
|
||||||
|
}
|
||||||
|
|
||||||
|
//DisplayTags and Liberate are live.
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PictureStorage_PictureCached(object sender, string pictureId)
|
||||||
|
{
|
||||||
|
if (pictureId == Book.PictureId)
|
||||||
|
{
|
||||||
|
Cover = WindowsDesktopUtilities.WinAudibleImageServer.GetImage(pictureId, FileManager.PictureSize._80x80);
|
||||||
|
FileManager.PictureStorage.PictureCached -= PictureStorage_PictureCached;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "") =>
|
||||||
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
|
|
||||||
|
#region Data Source properties
|
||||||
|
public Image Cover
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
displayValues[nameof(Title)] = book.Title;
|
return _cover;
|
||||||
return getSortName(book.Title);
|
|
||||||
}
|
}
|
||||||
}
|
private set
|
||||||
|
|
||||||
public string Authors => book.AuthorNames;
|
|
||||||
public string Narrators => book.NarratorNames;
|
|
||||||
|
|
||||||
public int Length
|
|
||||||
{
|
{
|
||||||
get
|
_cover = value;
|
||||||
{
|
NotifyPropertyChanged();
|
||||||
displayValues[nameof(Length)]
|
|
||||||
= book.LengthInMinutes == 0
|
|
||||||
? ""
|
|
||||||
: $"{book.LengthInMinutes / 60} hr {book.LengthInMinutes % 60} min";
|
|
||||||
|
|
||||||
return book.LengthInMinutes;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Series
|
public string ProductRating { get; }
|
||||||
{
|
public string PurchaseDate { get; }
|
||||||
get
|
public string MyRating { get; }
|
||||||
{
|
public string Series { get; }
|
||||||
displayValues[nameof(Series)] = book.SeriesNames;
|
public string Title { get; }
|
||||||
return getSortName(book.SeriesNames);
|
public string Length { get; }
|
||||||
}
|
public string Authors { get; }
|
||||||
}
|
public string Narrators { get; }
|
||||||
|
public string Category { get; }
|
||||||
|
public string Misc { get; }
|
||||||
|
public string Description { get; }
|
||||||
|
|
||||||
private static string[] sortPrefixIgnores { get; } = new[] { "the", "a", "an" };
|
public string DisplayTags => string.Join("\r\n", TagsEnumerated);
|
||||||
|
public (LiberatedState, PdfState) Liberate => (LibraryCommands.Liberated_Status(Book), LibraryCommands.Pdf_Status(Book));
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Data Sorting
|
||||||
|
|
||||||
|
private Dictionary<string, Func<object>> _compareValues { get; }
|
||||||
|
private static Dictionary<Type, IComparer> _objectComparers;
|
||||||
|
|
||||||
|
public object GetMemberValue(string propertyName) => _compareValues[propertyName]();
|
||||||
|
public IComparer GetComparer(Type propertyType) => _objectComparers[propertyType];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Instantiate comparers for every type needed to sort columns.
|
||||||
|
/// </summary>
|
||||||
|
static GridEntry()
|
||||||
|
{
|
||||||
|
_objectComparers = new Dictionary<Type, IComparer>()
|
||||||
|
{
|
||||||
|
{ typeof(string), new ObjectComparer<string>() },
|
||||||
|
{ typeof(int), new ObjectComparer<int>() },
|
||||||
|
{ typeof(float), new ObjectComparer<float>() },
|
||||||
|
{ typeof(DateTime), new ObjectComparer<DateTime>() },
|
||||||
|
{ typeof(LiberatedState), new ObjectComparer<LiberatedState>() },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Create getters for all member values by name
|
||||||
|
/// </summary>
|
||||||
|
Dictionary<string, Func<object>> CreatePropertyValueDictionary() => new()
|
||||||
|
{
|
||||||
|
{ nameof(Title), () => getSortName(Book.Title)},
|
||||||
|
{ nameof(Series),() => getSortName(Book.SeriesNames)},
|
||||||
|
{ nameof(Length), () => Book.LengthInMinutes},
|
||||||
|
{ nameof(MyRating), () => Book.UserDefinedItem.Rating.FirstScore},
|
||||||
|
{ nameof(PurchaseDate), () => LibraryBook.DateAdded},
|
||||||
|
{ nameof(ProductRating), () => Book.Rating.FirstScore},
|
||||||
|
{ nameof(Authors), () => Authors},
|
||||||
|
{ nameof(Narrators), () => Narrators},
|
||||||
|
{ nameof(Description), () => Description},
|
||||||
|
{ nameof(Category), () => Category},
|
||||||
|
{ nameof(Misc), () => Misc},
|
||||||
|
{ nameof(DisplayTags), () => DisplayTags},
|
||||||
|
{ nameof(Liberate), () => Liberate.Item1}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly string[] sortPrefixIgnores = { "the", "a", "an" };
|
||||||
private static string getSortName(string unformattedName)
|
private static string getSortName(string unformattedName)
|
||||||
{
|
{
|
||||||
var sortName = unformattedName
|
var sortName = unformattedName
|
||||||
@ -95,104 +168,115 @@ namespace LibationWinForms
|
|||||||
return sortName;
|
return sortName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string descriptionCache = null;
|
#endregion
|
||||||
public string Description
|
|
||||||
|
#region Static library display functions
|
||||||
|
|
||||||
|
public static (string mouseoverText, Bitmap buttonImage) GetLiberateDisplay(LiberatedState liberatedStatus, PdfState pdfStatus)
|
||||||
{
|
{
|
||||||
get
|
string text;
|
||||||
|
Bitmap image;
|
||||||
|
|
||||||
|
// get mouseover text
|
||||||
{
|
{
|
||||||
// HtmlAgilityPack is expensive. cache results
|
var libState = liberatedStatus switch
|
||||||
if (descriptionCache is null)
|
|
||||||
{
|
{
|
||||||
if (book.Description is null)
|
LiberatedState.Liberated => "Liberated",
|
||||||
descriptionCache = "";
|
LiberatedState.PartialDownload => "File has been at least\r\npartially downloaded",
|
||||||
else
|
LiberatedState.NotDownloaded => "Book NOT downloaded",
|
||||||
|
_ => throw new Exception("Unexpected liberation state")
|
||||||
|
};
|
||||||
|
|
||||||
|
var pdfState = pdfStatus switch
|
||||||
|
{
|
||||||
|
PdfState.Downloaded => "\r\nPDF downloaded",
|
||||||
|
PdfState.NotDownloaded => "\r\nPDF NOT downloaded",
|
||||||
|
PdfState.NoPdf => "",
|
||||||
|
_ => throw new Exception("Unexpected PDF state")
|
||||||
|
};
|
||||||
|
|
||||||
|
text = libState + pdfState;
|
||||||
|
|
||||||
|
if (liberatedStatus == LiberatedState.NotDownloaded ||
|
||||||
|
liberatedStatus == LiberatedState.PartialDownload ||
|
||||||
|
pdfStatus == PdfState.NotDownloaded)
|
||||||
|
text += "\r\nClick to complete";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// get image
|
||||||
|
{
|
||||||
|
var image_lib
|
||||||
|
= liberatedStatus == LiberatedState.NotDownloaded ? "red"
|
||||||
|
: liberatedStatus == LiberatedState.PartialDownload ? "yellow"
|
||||||
|
: liberatedStatus == LiberatedState.Liberated ? "green"
|
||||||
|
: throw new Exception("Unexpected liberation state");
|
||||||
|
var image_pdf
|
||||||
|
= pdfStatus == PdfState.NoPdf ? ""
|
||||||
|
: pdfStatus == PdfState.NotDownloaded ? "_pdf_no"
|
||||||
|
: pdfStatus == PdfState.Downloaded ? "_pdf_yes"
|
||||||
|
: throw new Exception("Unexpected PDF state");
|
||||||
|
|
||||||
|
image = (Bitmap)Properties.Resources.ResourceManager.GetObject($"liberate_{image_lib}{image_pdf}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (text, image);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This information should not change during <see cref="GridEntry"/> lifetime, so call only once.
|
||||||
|
/// </summary>
|
||||||
|
private static string GetDescriptionDisplay(Book book)
|
||||||
{
|
{
|
||||||
var doc = new HtmlAgilityPack.HtmlDocument();
|
var doc = new HtmlAgilityPack.HtmlDocument();
|
||||||
doc.LoadHtml(book.Description);
|
doc.LoadHtml(book.Description);
|
||||||
var noHtml = doc.DocumentNode.InnerText;
|
var noHtml = doc.DocumentNode.InnerText;
|
||||||
descriptionCache
|
return
|
||||||
= noHtml.Length < 63
|
noHtml.Length < 63?
|
||||||
? noHtml
|
noHtml :
|
||||||
: noHtml.Substring(0, 60) + "...";
|
noHtml.Substring(0, 60) + "...";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return descriptionCache;
|
/// <summary>
|
||||||
}
|
/// This information should not change during <see cref="GridEntry"/> lifetime, so call only once.
|
||||||
}
|
/// </summary>
|
||||||
|
private static string GetMiscDisplay(LibraryBook libraryBook)
|
||||||
public string Category => string.Join(" > ", book.CategoriesNames);
|
|
||||||
|
|
||||||
// star ratings retain numeric value but display star text. this is needed because just using star text doesn't sort correctly:
|
|
||||||
// - star
|
|
||||||
// - star star
|
|
||||||
// - star 1/2
|
|
||||||
|
|
||||||
public string Product_Rating
|
|
||||||
{
|
{
|
||||||
get
|
|
||||||
{
|
|
||||||
displayValues[nameof(Product_Rating)] = starString(book.Rating);
|
|
||||||
return firstScore(book.Rating);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Purchase_Date
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
displayValues[nameof(Purchase_Date)] = libraryBook.DateAdded.ToString("d");
|
|
||||||
return libraryBook.DateAdded.ToString("yyyy-MM-dd HH:mm:ss");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string My_Rating
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
displayValues[nameof(My_Rating)] = starString(book.UserDefinedItem.Rating);
|
|
||||||
return firstScore(book.UserDefinedItem.Rating);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private string starString(Rating rating)
|
|
||||||
=> (rating?.FirstScore != null && rating?.FirstScore > 0f)
|
|
||||||
? rating?.ToStarString()
|
|
||||||
: "";
|
|
||||||
private string firstScore(Rating rating) => rating?.FirstScore.ToString("0.0");
|
|
||||||
|
|
||||||
// max 5 text rows
|
// max 5 text rows
|
||||||
public string Misc
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var details = new List<string>();
|
var details = new List<string>();
|
||||||
|
|
||||||
var locale
|
var locale
|
||||||
= string.IsNullOrWhiteSpace(book.Locale)
|
= string.IsNullOrWhiteSpace(libraryBook.Book.Locale)
|
||||||
? "[unknown]"
|
? "[unknown]"
|
||||||
: book.Locale;
|
: libraryBook.Book.Locale;
|
||||||
var acct
|
var acct
|
||||||
= string.IsNullOrWhiteSpace(libraryBook.Account)
|
= string.IsNullOrWhiteSpace(libraryBook.Account)
|
||||||
? "[unknown]"
|
? "[unknown]"
|
||||||
: libraryBook.Account;
|
: libraryBook.Account;
|
||||||
details.Add($"Account: {locale} - {acct}");
|
details.Add($"Account: {locale} - {acct}");
|
||||||
|
|
||||||
if (book.HasPdf)
|
if (libraryBook.Book.HasPdf)
|
||||||
details.Add("Has PDF");
|
details.Add("Has PDF");
|
||||||
if (book.IsAbridged)
|
if (libraryBook.Book.IsAbridged)
|
||||||
details.Add("Abridged");
|
details.Add("Abridged");
|
||||||
if (book.DatePublished.HasValue)
|
if (libraryBook.Book.DatePublished.HasValue)
|
||||||
details.Add($"Date pub'd: {book.DatePublished.Value:MM/dd/yyyy}");
|
details.Add($"Date pub'd: {libraryBook.Book.DatePublished.Value:MM/dd/yyyy}");
|
||||||
// this goes last since it's most likely to have a line-break
|
// this goes last since it's most likely to have a line-break
|
||||||
if (!string.IsNullOrWhiteSpace(book.Publisher))
|
if (!string.IsNullOrWhiteSpace(libraryBook.Book.Publisher))
|
||||||
details.Add($"Pub: {book.Publisher.Trim()}");
|
details.Add($"Pub: {libraryBook.Book.Publisher.Trim()}");
|
||||||
|
|
||||||
if (!details.Any())
|
if (!details.Any())
|
||||||
return "[details not imported]";
|
return "[details not imported]";
|
||||||
|
|
||||||
return string.Join("\r\n", details);
|
return string.Join("\r\n", details);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private static string GetStarString(Rating rating)
|
||||||
|
=> (rating?.FirstScore != null && rating?.FirstScore > 0f)
|
||||||
|
? rating?.ToStarString()
|
||||||
|
: "";
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
LibationWinForms/IObjectMemberComparable.cs
Normal file
11
LibationWinForms/IObjectMemberComparable.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace LibationWinForms
|
||||||
|
{
|
||||||
|
interface IObjectMemberComparable
|
||||||
|
{
|
||||||
|
IComparer GetComparer(Type propertyType);
|
||||||
|
object GetMemberValue(string valueName);
|
||||||
|
}
|
||||||
|
}
|
||||||
30
LibationWinForms/LiberateDataGridViewImageButtonColumn.cs
Normal file
30
LibationWinForms/LiberateDataGridViewImageButtonColumn.cs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
using ApplicationServices;
|
||||||
|
using System;
|
||||||
|
using System.Drawing;
|
||||||
|
using System.Windows.Forms;
|
||||||
|
|
||||||
|
namespace LibationWinForms
|
||||||
|
{
|
||||||
|
public class LiberateDataGridViewImageButtonColumn : DataGridViewImageButtonColumn
|
||||||
|
{
|
||||||
|
protected override DataGridViewImageButtonCell NewCell()
|
||||||
|
=> new LiberateDataGridViewImageButtonCell();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class LiberateDataGridViewImageButtonCell : DataGridViewImageButtonCell
|
||||||
|
{
|
||||||
|
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates elementState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
|
||||||
|
{
|
||||||
|
base.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, null, null, null, cellStyle, advancedBorderStyle, paintParts);
|
||||||
|
|
||||||
|
if (value is (LiberatedState liberatedState, PdfState pdfState))
|
||||||
|
{
|
||||||
|
(string mouseoverText, Bitmap buttonImage) = GridEntry.GetLiberateDisplay(liberatedState, pdfState);
|
||||||
|
|
||||||
|
DrawImage(graphics, buttonImage, cellBounds);
|
||||||
|
|
||||||
|
ToolTipText = mouseoverText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
LibationWinForms/ObjectComparer[T].cs
Normal file
10
LibationWinForms/ObjectComparer[T].cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
namespace LibationWinForms
|
||||||
|
{
|
||||||
|
class ObjectComparer<T> : IComparer where T : IComparable
|
||||||
|
{
|
||||||
|
public int Compare(object x, object y) => ((T)x).CompareTo((T)y);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
LibationWinForms/ObjectMemberComparer[T].cs
Normal file
21
LibationWinForms/ObjectMemberComparer[T].cs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace LibationWinForms
|
||||||
|
{
|
||||||
|
class ObjectMemberComparer<T> : IComparer<T> where T : IObjectMemberComparable
|
||||||
|
{
|
||||||
|
public ListSortDirection Direction { get; set; } = ListSortDirection.Ascending;
|
||||||
|
public string PropertyName { get; set; }
|
||||||
|
|
||||||
|
public int Compare(T x, T y)
|
||||||
|
{
|
||||||
|
var val1 = x.GetMemberValue(PropertyName);
|
||||||
|
var val2 = y.GetMemberValue(PropertyName);
|
||||||
|
|
||||||
|
return DirMult * x.GetComparer(val1.GetType()).Compare(val1, val2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int DirMult => Direction == ListSortDirection.Descending ? -1 : 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
115
LibationWinForms/ProductsGrid.Designer.cs
generated
115
LibationWinForms/ProductsGrid.Designer.cs
generated
@ -29,8 +29,10 @@
|
|||||||
private void InitializeComponent()
|
private void InitializeComponent()
|
||||||
{
|
{
|
||||||
this.components = new System.ComponentModel.Container();
|
this.components = new System.ComponentModel.Container();
|
||||||
|
System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle();
|
||||||
this.gridEntryBindingSource = new System.Windows.Forms.BindingSource(this.components);
|
this.gridEntryBindingSource = new System.Windows.Forms.BindingSource(this.components);
|
||||||
this.gridEntryDataGridView = new System.Windows.Forms.DataGridView();
|
this.gridEntryDataGridView = new System.Windows.Forms.DataGridView();
|
||||||
|
this.dataGridViewImageButtonBoxColumn1 = new LibationWinForms.LiberateDataGridViewImageButtonColumn();
|
||||||
this.dataGridViewImageColumn1 = new System.Windows.Forms.DataGridViewImageColumn();
|
this.dataGridViewImageColumn1 = new System.Windows.Forms.DataGridViewImageColumn();
|
||||||
this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
this.dataGridViewTextBoxColumn1 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
this.dataGridViewTextBoxColumn2 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
@ -39,10 +41,11 @@
|
|||||||
this.dataGridViewTextBoxColumn5 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
this.dataGridViewTextBoxColumn5 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
this.dataGridViewTextBoxColumn6 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
this.dataGridViewTextBoxColumn6 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
this.dataGridViewTextBoxColumn7 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
this.dataGridViewTextBoxColumn7 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
this.dataGridViewTextBoxColumn8 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
this.ProductRating = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
this.dataGridViewTextBoxColumn9 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
this.PurchaseDate = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
this.dataGridViewTextBoxColumn10 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
this.MyRating = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
this.dataGridViewTextBoxColumn11 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
this.dataGridViewTextBoxColumn11 = new System.Windows.Forms.DataGridViewTextBoxColumn();
|
||||||
|
this.dataGridViewImageButtonBoxColumn2 = new LibationWinForms.EditTagsDataGridViewImageButtonColumn();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).BeginInit();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).BeginInit();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
@ -53,9 +56,12 @@
|
|||||||
//
|
//
|
||||||
// gridEntryDataGridView
|
// gridEntryDataGridView
|
||||||
//
|
//
|
||||||
|
this.gridEntryDataGridView.AllowUserToAddRows = false;
|
||||||
|
this.gridEntryDataGridView.AllowUserToDeleteRows = false;
|
||||||
this.gridEntryDataGridView.AutoGenerateColumns = 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.dataGridViewImageButtonBoxColumn1,
|
||||||
this.dataGridViewImageColumn1,
|
this.dataGridViewImageColumn1,
|
||||||
this.dataGridViewTextBoxColumn1,
|
this.dataGridViewTextBoxColumn1,
|
||||||
this.dataGridViewTextBoxColumn2,
|
this.dataGridViewTextBoxColumn2,
|
||||||
@ -64,22 +70,49 @@
|
|||||||
this.dataGridViewTextBoxColumn5,
|
this.dataGridViewTextBoxColumn5,
|
||||||
this.dataGridViewTextBoxColumn6,
|
this.dataGridViewTextBoxColumn6,
|
||||||
this.dataGridViewTextBoxColumn7,
|
this.dataGridViewTextBoxColumn7,
|
||||||
this.dataGridViewTextBoxColumn8,
|
this.ProductRating,
|
||||||
this.dataGridViewTextBoxColumn9,
|
this.PurchaseDate,
|
||||||
this.dataGridViewTextBoxColumn10,
|
this.MyRating,
|
||||||
this.dataGridViewTextBoxColumn11});
|
this.dataGridViewTextBoxColumn11,
|
||||||
|
this.dataGridViewImageButtonBoxColumn2});
|
||||||
this.gridEntryDataGridView.DataSource = this.gridEntryBindingSource;
|
this.gridEntryDataGridView.DataSource = this.gridEntryBindingSource;
|
||||||
this.gridEntryDataGridView.Location = new System.Drawing.Point(54, 58);
|
dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft;
|
||||||
|
dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Window;
|
||||||
|
dataGridViewCellStyle1.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
|
||||||
|
dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.ControlText;
|
||||||
|
dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight;
|
||||||
|
dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText;
|
||||||
|
dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True;
|
||||||
|
this.gridEntryDataGridView.DefaultCellStyle = dataGridViewCellStyle1;
|
||||||
|
this.gridEntryDataGridView.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||||
|
this.gridEntryDataGridView.Location = new System.Drawing.Point(0, 0);
|
||||||
|
this.gridEntryDataGridView.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
|
||||||
this.gridEntryDataGridView.Name = "gridEntryDataGridView";
|
this.gridEntryDataGridView.Name = "gridEntryDataGridView";
|
||||||
this.gridEntryDataGridView.Size = new System.Drawing.Size(300, 220);
|
this.gridEntryDataGridView.ReadOnly = true;
|
||||||
|
this.gridEntryDataGridView.RowHeadersVisible = false;
|
||||||
|
this.gridEntryDataGridView.RowTemplate.Height = 82;
|
||||||
|
this.gridEntryDataGridView.Size = new System.Drawing.Size(1503, 380);
|
||||||
this.gridEntryDataGridView.TabIndex = 0;
|
this.gridEntryDataGridView.TabIndex = 0;
|
||||||
//
|
//
|
||||||
|
// dataGridViewImageButtonBoxColumn1
|
||||||
|
//
|
||||||
|
this.dataGridViewImageButtonBoxColumn1.DataPropertyName = "Liberate";
|
||||||
|
this.dataGridViewImageButtonBoxColumn1.HeaderText = "Liberate";
|
||||||
|
this.dataGridViewImageButtonBoxColumn1.Name = "dataGridViewImageButtonBoxColumn1";
|
||||||
|
this.dataGridViewImageButtonBoxColumn1.ReadOnly = true;
|
||||||
|
this.dataGridViewImageButtonBoxColumn1.Resizable = System.Windows.Forms.DataGridViewTriState.False;
|
||||||
|
this.dataGridViewImageButtonBoxColumn1.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
|
||||||
|
this.dataGridViewImageButtonBoxColumn1.Width = 70;
|
||||||
|
//
|
||||||
// dataGridViewImageColumn1
|
// dataGridViewImageColumn1
|
||||||
//
|
//
|
||||||
this.dataGridViewImageColumn1.DataPropertyName = "Cover";
|
this.dataGridViewImageColumn1.DataPropertyName = "Cover";
|
||||||
this.dataGridViewImageColumn1.HeaderText = "Cover";
|
this.dataGridViewImageColumn1.HeaderText = "Cover";
|
||||||
this.dataGridViewImageColumn1.Name = "dataGridViewImageColumn1";
|
this.dataGridViewImageColumn1.Name = "dataGridViewImageColumn1";
|
||||||
this.dataGridViewImageColumn1.ReadOnly = true;
|
this.dataGridViewImageColumn1.ReadOnly = true;
|
||||||
|
this.dataGridViewImageColumn1.Resizable = System.Windows.Forms.DataGridViewTriState.False;
|
||||||
|
this.dataGridViewImageColumn1.ToolTipText = "Cover Art";
|
||||||
|
this.dataGridViewImageColumn1.Width = 80;
|
||||||
//
|
//
|
||||||
// dataGridViewTextBoxColumn1
|
// dataGridViewTextBoxColumn1
|
||||||
//
|
//
|
||||||
@ -87,6 +120,8 @@
|
|||||||
this.dataGridViewTextBoxColumn1.HeaderText = "Title";
|
this.dataGridViewTextBoxColumn1.HeaderText = "Title";
|
||||||
this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1";
|
this.dataGridViewTextBoxColumn1.Name = "dataGridViewTextBoxColumn1";
|
||||||
this.dataGridViewTextBoxColumn1.ReadOnly = true;
|
this.dataGridViewTextBoxColumn1.ReadOnly = true;
|
||||||
|
this.dataGridViewTextBoxColumn1.ToolTipText = "Book Title";
|
||||||
|
this.dataGridViewTextBoxColumn1.Width = 200;
|
||||||
//
|
//
|
||||||
// dataGridViewTextBoxColumn2
|
// dataGridViewTextBoxColumn2
|
||||||
//
|
//
|
||||||
@ -94,6 +129,7 @@
|
|||||||
this.dataGridViewTextBoxColumn2.HeaderText = "Authors";
|
this.dataGridViewTextBoxColumn2.HeaderText = "Authors";
|
||||||
this.dataGridViewTextBoxColumn2.Name = "dataGridViewTextBoxColumn2";
|
this.dataGridViewTextBoxColumn2.Name = "dataGridViewTextBoxColumn2";
|
||||||
this.dataGridViewTextBoxColumn2.ReadOnly = true;
|
this.dataGridViewTextBoxColumn2.ReadOnly = true;
|
||||||
|
this.dataGridViewTextBoxColumn2.ToolTipText = "Book Author(s)";
|
||||||
//
|
//
|
||||||
// dataGridViewTextBoxColumn3
|
// dataGridViewTextBoxColumn3
|
||||||
//
|
//
|
||||||
@ -101,6 +137,7 @@
|
|||||||
this.dataGridViewTextBoxColumn3.HeaderText = "Narrators";
|
this.dataGridViewTextBoxColumn3.HeaderText = "Narrators";
|
||||||
this.dataGridViewTextBoxColumn3.Name = "dataGridViewTextBoxColumn3";
|
this.dataGridViewTextBoxColumn3.Name = "dataGridViewTextBoxColumn3";
|
||||||
this.dataGridViewTextBoxColumn3.ReadOnly = true;
|
this.dataGridViewTextBoxColumn3.ReadOnly = true;
|
||||||
|
this.dataGridViewTextBoxColumn3.ToolTipText = "Audiobook Narrator(s)";
|
||||||
//
|
//
|
||||||
// dataGridViewTextBoxColumn4
|
// dataGridViewTextBoxColumn4
|
||||||
//
|
//
|
||||||
@ -108,6 +145,7 @@
|
|||||||
this.dataGridViewTextBoxColumn4.HeaderText = "Length";
|
this.dataGridViewTextBoxColumn4.HeaderText = "Length";
|
||||||
this.dataGridViewTextBoxColumn4.Name = "dataGridViewTextBoxColumn4";
|
this.dataGridViewTextBoxColumn4.Name = "dataGridViewTextBoxColumn4";
|
||||||
this.dataGridViewTextBoxColumn4.ReadOnly = true;
|
this.dataGridViewTextBoxColumn4.ReadOnly = true;
|
||||||
|
this.dataGridViewTextBoxColumn4.ToolTipText = "Recording Length";
|
||||||
//
|
//
|
||||||
// dataGridViewTextBoxColumn5
|
// dataGridViewTextBoxColumn5
|
||||||
//
|
//
|
||||||
@ -115,6 +153,7 @@
|
|||||||
this.dataGridViewTextBoxColumn5.HeaderText = "Series";
|
this.dataGridViewTextBoxColumn5.HeaderText = "Series";
|
||||||
this.dataGridViewTextBoxColumn5.Name = "dataGridViewTextBoxColumn5";
|
this.dataGridViewTextBoxColumn5.Name = "dataGridViewTextBoxColumn5";
|
||||||
this.dataGridViewTextBoxColumn5.ReadOnly = true;
|
this.dataGridViewTextBoxColumn5.ReadOnly = true;
|
||||||
|
this.dataGridViewTextBoxColumn5.ToolTipText = "Book Series";
|
||||||
//
|
//
|
||||||
// dataGridViewTextBoxColumn6
|
// dataGridViewTextBoxColumn6
|
||||||
//
|
//
|
||||||
@ -130,26 +169,28 @@
|
|||||||
this.dataGridViewTextBoxColumn7.Name = "dataGridViewTextBoxColumn7";
|
this.dataGridViewTextBoxColumn7.Name = "dataGridViewTextBoxColumn7";
|
||||||
this.dataGridViewTextBoxColumn7.ReadOnly = true;
|
this.dataGridViewTextBoxColumn7.ReadOnly = true;
|
||||||
//
|
//
|
||||||
// dataGridViewTextBoxColumn8
|
// ProductRating
|
||||||
//
|
//
|
||||||
this.dataGridViewTextBoxColumn8.DataPropertyName = "Product_Rating";
|
this.ProductRating.DataPropertyName = "ProductRating";
|
||||||
this.dataGridViewTextBoxColumn8.HeaderText = "Product_Rating";
|
this.ProductRating.HeaderText = "Product Rating";
|
||||||
this.dataGridViewTextBoxColumn8.Name = "dataGridViewTextBoxColumn8";
|
this.ProductRating.Name = "ProductRating";
|
||||||
this.dataGridViewTextBoxColumn8.ReadOnly = true;
|
this.ProductRating.ReadOnly = true;
|
||||||
|
this.ProductRating.Width = 108;
|
||||||
//
|
//
|
||||||
// dataGridViewTextBoxColumn9
|
// PurchaseDate
|
||||||
//
|
//
|
||||||
this.dataGridViewTextBoxColumn9.DataPropertyName = "Purchase_Date";
|
this.PurchaseDate.DataPropertyName = "PurchaseDate";
|
||||||
this.dataGridViewTextBoxColumn9.HeaderText = "Purchase_Date";
|
this.PurchaseDate.HeaderText = "Purchase Date";
|
||||||
this.dataGridViewTextBoxColumn9.Name = "dataGridViewTextBoxColumn9";
|
this.PurchaseDate.Name = "PurchaseDate";
|
||||||
this.dataGridViewTextBoxColumn9.ReadOnly = true;
|
this.PurchaseDate.ReadOnly = true;
|
||||||
//
|
//
|
||||||
// dataGridViewTextBoxColumn10
|
// MyRating
|
||||||
//
|
//
|
||||||
this.dataGridViewTextBoxColumn10.DataPropertyName = "My_Rating";
|
this.MyRating.DataPropertyName = "MyRating";
|
||||||
this.dataGridViewTextBoxColumn10.HeaderText = "My_Rating";
|
this.MyRating.HeaderText = "My Rating";
|
||||||
this.dataGridViewTextBoxColumn10.Name = "dataGridViewTextBoxColumn10";
|
this.MyRating.Name = "MyRating";
|
||||||
this.dataGridViewTextBoxColumn10.ReadOnly = true;
|
this.MyRating.ReadOnly = true;
|
||||||
|
this.MyRating.Width = 108;
|
||||||
//
|
//
|
||||||
// dataGridViewTextBoxColumn11
|
// dataGridViewTextBoxColumn11
|
||||||
//
|
//
|
||||||
@ -157,14 +198,25 @@
|
|||||||
this.dataGridViewTextBoxColumn11.HeaderText = "Misc";
|
this.dataGridViewTextBoxColumn11.HeaderText = "Misc";
|
||||||
this.dataGridViewTextBoxColumn11.Name = "dataGridViewTextBoxColumn11";
|
this.dataGridViewTextBoxColumn11.Name = "dataGridViewTextBoxColumn11";
|
||||||
this.dataGridViewTextBoxColumn11.ReadOnly = true;
|
this.dataGridViewTextBoxColumn11.ReadOnly = true;
|
||||||
|
this.dataGridViewTextBoxColumn11.Width = 135;
|
||||||
|
//
|
||||||
|
// dataGridViewImageButtonBoxColumn2
|
||||||
|
//
|
||||||
|
this.dataGridViewImageButtonBoxColumn2.DataPropertyName = "DisplayTags";
|
||||||
|
this.dataGridViewImageButtonBoxColumn2.HeaderText = "Edit Tags";
|
||||||
|
this.dataGridViewImageButtonBoxColumn2.Name = "dataGridViewImageButtonBoxColumn2";
|
||||||
|
this.dataGridViewImageButtonBoxColumn2.ReadOnly = true;
|
||||||
|
this.dataGridViewImageButtonBoxColumn2.Resizable = System.Windows.Forms.DataGridViewTriState.False;
|
||||||
|
this.dataGridViewImageButtonBoxColumn2.SortMode = System.Windows.Forms.DataGridViewColumnSortMode.Automatic;
|
||||||
//
|
//
|
||||||
// ProductsGrid
|
// ProductsGrid
|
||||||
//
|
//
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
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.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(434, 329);
|
this.Size = new System.Drawing.Size(1503, 380);
|
||||||
((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.gridEntryBindingSource)).EndInit();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.gridEntryDataGridView)).EndInit();
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
@ -175,6 +227,10 @@
|
|||||||
|
|
||||||
private System.Windows.Forms.BindingSource gridEntryBindingSource;
|
private System.Windows.Forms.BindingSource gridEntryBindingSource;
|
||||||
private System.Windows.Forms.DataGridView gridEntryDataGridView;
|
private System.Windows.Forms.DataGridView gridEntryDataGridView;
|
||||||
|
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn8;
|
||||||
|
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn9;
|
||||||
|
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn10;
|
||||||
|
private LiberateDataGridViewImageButtonColumn dataGridViewImageButtonBoxColumn1;
|
||||||
private System.Windows.Forms.DataGridViewImageColumn dataGridViewImageColumn1;
|
private System.Windows.Forms.DataGridViewImageColumn dataGridViewImageColumn1;
|
||||||
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1;
|
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn1;
|
||||||
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2;
|
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn2;
|
||||||
@ -183,9 +239,10 @@
|
|||||||
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn5;
|
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn5;
|
||||||
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn6;
|
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn6;
|
||||||
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn7;
|
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn7;
|
||||||
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn8;
|
private System.Windows.Forms.DataGridViewTextBoxColumn ProductRating;
|
||||||
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn9;
|
private System.Windows.Forms.DataGridViewTextBoxColumn PurchaseDate;
|
||||||
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn10;
|
private System.Windows.Forms.DataGridViewTextBoxColumn MyRating;
|
||||||
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn11;
|
private System.Windows.Forms.DataGridViewTextBoxColumn dataGridViewTextBoxColumn11;
|
||||||
|
private EditTagsDataGridViewImageButtonColumn dataGridViewImageButtonBoxColumn2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using ApplicationServices;
|
using ApplicationServices;
|
||||||
using DataLayer;
|
using DataLayer;
|
||||||
using Dinah.Core;
|
using Dinah.Core;
|
||||||
using Dinah.Core.Collections.Generic;
|
|
||||||
using Dinah.Core.DataBinding;
|
|
||||||
using Dinah.Core.Windows.Forms;
|
using Dinah.Core.Windows.Forms;
|
||||||
using LibationWinForms.Dialogs;
|
using LibationWinForms.Dialogs;
|
||||||
|
|
||||||
@ -30,158 +29,54 @@ namespace LibationWinForms
|
|||||||
public event EventHandler<int> VisibleCountChanged;
|
public event EventHandler<int> VisibleCountChanged;
|
||||||
public event EventHandler BackupCountsChanged;
|
public event EventHandler BackupCountsChanged;
|
||||||
|
|
||||||
private const string EDIT_TAGS = "Edit Tags";
|
|
||||||
private const string LIBERATE = "Liberate";
|
|
||||||
|
|
||||||
// alias
|
// alias
|
||||||
private DataGridView dataGridView => gridEntryDataGridView;
|
private DataGridView _dataGridView => gridEntryDataGridView;
|
||||||
|
|
||||||
public ProductsGrid()
|
public ProductsGrid()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
formatDataGridView();
|
|
||||||
addLiberateButtons();
|
|
||||||
addEditTagsButtons();
|
|
||||||
formatColumns();
|
|
||||||
|
|
||||||
manageLiveImageUpdateSubscriptions();
|
// sorting breaks filters. must reapply filters after sorting
|
||||||
|
_dataGridView.Sorted += (_, __) => Filter();
|
||||||
|
_dataGridView.CellFormatting += HiddenFormatting;
|
||||||
|
_dataGridView.CellContentClick += DataGridView_CellContentClick;
|
||||||
|
|
||||||
enableDoubleBuffering();
|
EnableDoubleBuffering();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableDoubleBuffering()
|
private void EnableDoubleBuffering()
|
||||||
{
|
{
|
||||||
var propertyInfo = dataGridView.GetType().GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
var propertyInfo = _dataGridView.GetType().GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
||||||
|
|
||||||
//var before = (bool)propertyInfo.GetValue(dataGridView);
|
//var before = (bool)propertyInfo.GetValue(dataGridView);
|
||||||
propertyInfo.SetValue(dataGridView, true, null);
|
propertyInfo.SetValue(_dataGridView, true, null);
|
||||||
//var after = (bool)propertyInfo.GetValue(dataGridView);
|
//var after = (bool)propertyInfo.GetValue(dataGridView);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void formatDataGridView()
|
#region Button controls
|
||||||
|
|
||||||
|
private async void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
|
||||||
{
|
{
|
||||||
dataGridView.Dock = DockStyle.Fill;
|
// handle grid button click: https://stackoverflow.com/a/13687844
|
||||||
dataGridView.AllowUserToAddRows = false;
|
if (e.RowIndex < 0 || _dataGridView.Columns[e.ColumnIndex] is not DataGridViewButtonColumn)
|
||||||
dataGridView.AllowUserToDeleteRows = false;
|
|
||||||
dataGridView.AutoGenerateColumns = false;
|
|
||||||
dataGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize;
|
|
||||||
dataGridView.DefaultCellStyle.WrapMode = DataGridViewTriState.True;
|
|
||||||
dataGridView.ReadOnly = true;
|
|
||||||
dataGridView.RowHeadersVisible = false;
|
|
||||||
|
|
||||||
// adjust height for 80x80 pictures.
|
|
||||||
// this must be done before databinding. or can alter later by iterating through rows
|
|
||||||
dataGridView.RowTemplate.Height = 82;
|
|
||||||
dataGridView.CellFormatting += replaceFormatted;
|
|
||||||
dataGridView.CellFormatting += hiddenFormatting;
|
|
||||||
|
|
||||||
// sorting breaks filters. must reapply filters after sorting
|
|
||||||
dataGridView.Sorted += (_, __) => filter();
|
|
||||||
}
|
|
||||||
|
|
||||||
#region format text cells. ie: not buttons
|
|
||||||
private void replaceFormatted(object sender, DataGridViewCellFormattingEventArgs e)
|
|
||||||
{
|
|
||||||
var col = ((DataGridView)sender).Columns[e.ColumnIndex];
|
|
||||||
if (col is DataGridViewTextBoxColumn textCol && getGridEntry(e.RowIndex).TryDisplayValue(textCol.Name, out string value))
|
|
||||||
{
|
|
||||||
// DO NOT DO THIS: getCell(e).Value = value;
|
|
||||||
// it's the wrong way and will infinitely call CellFormatting on each assign
|
|
||||||
|
|
||||||
// this is the correct way. will actually set FormattedValue (and EditedFormattedValue) while leaving Value as-is for sorting
|
|
||||||
e.Value = value;
|
|
||||||
|
|
||||||
getCell(e).ToolTipText = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void hiddenFormatting(object sender, DataGridViewCellFormattingEventArgs e)
|
|
||||||
{
|
|
||||||
var dgv = (DataGridView)sender;
|
|
||||||
// no action needed for buttons
|
|
||||||
if (e.RowIndex < 0 || dgv.Columns[e.ColumnIndex] is DataGridViewButtonColumn)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var isHidden = getGridEntry(e.RowIndex).TagsEnumerated.Contains("hidden");
|
var liveGridEntry = getGridEntry(e.RowIndex);
|
||||||
|
|
||||||
getCell(e).Style
|
switch (_dataGridView.Columns[e.ColumnIndex].DataPropertyName)
|
||||||
= isHidden
|
|
||||||
? new DataGridViewCellStyle { ForeColor = Color.LightGray }
|
|
||||||
: dgv.DefaultCellStyle;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region liberation buttons
|
|
||||||
private void addLiberateButtons()
|
|
||||||
{
|
{
|
||||||
dataGridView.Columns.Insert(0, new DataGridViewButtonColumn { HeaderText = LIBERATE });
|
case GridEntry.LIBERATE_COLUMN_NAME:
|
||||||
|
await Liberate_Click(liveGridEntry);
|
||||||
dataGridView.CellPainting += liberate_Paint;
|
break;
|
||||||
dataGridView.CellContentClick += liberate_Click;
|
case GridEntry.EDIT_TAGS_COLUMN_NAME:
|
||||||
}
|
EditTags_Click(liveGridEntry);
|
||||||
|
break;
|
||||||
private void liberate_Paint(object sender, DataGridViewCellPaintingEventArgs e)
|
|
||||||
{
|
|
||||||
if (!isColumnValid(e, LIBERATE))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var cell = getCell(e);
|
|
||||||
var gridEntry = getGridEntry(e.RowIndex);
|
|
||||||
var liberatedStatus = gridEntry.Liberated_Status;
|
|
||||||
var pdfStatus = gridEntry.Pdf_Status;
|
|
||||||
|
|
||||||
// mouseover text
|
|
||||||
{
|
|
||||||
var libState = liberatedStatus switch
|
|
||||||
{
|
|
||||||
LiberatedState.Liberated => "Liberated",
|
|
||||||
LiberatedState.PartialDownload => "File has been at least\r\npartially downloaded",
|
|
||||||
LiberatedState.NotDownloaded => "Book NOT downloaded",
|
|
||||||
_ => throw new Exception("Unexpected liberation state")
|
|
||||||
};
|
|
||||||
|
|
||||||
var pdfState = pdfStatus switch
|
|
||||||
{
|
|
||||||
PdfState.Downloaded => "\r\nPDF downloaded",
|
|
||||||
PdfState.NotDownloaded => "\r\nPDF NOT downloaded",
|
|
||||||
PdfState.NoPdf => "",
|
|
||||||
_ => throw new Exception("Unexpected PDF state")
|
|
||||||
};
|
|
||||||
|
|
||||||
var text = libState + pdfState;
|
|
||||||
|
|
||||||
if (liberatedStatus == LiberatedState.NotDownloaded ||
|
|
||||||
liberatedStatus == LiberatedState.PartialDownload ||
|
|
||||||
pdfStatus == PdfState.NotDownloaded)
|
|
||||||
text += "\r\nClick to complete";
|
|
||||||
|
|
||||||
//DEBUG//cell.Value = text;
|
|
||||||
cell.ToolTipText = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
// draw img
|
|
||||||
{
|
|
||||||
var image_lib
|
|
||||||
= liberatedStatus == LiberatedState.NotDownloaded ? "red"
|
|
||||||
: liberatedStatus == LiberatedState.PartialDownload ? "yellow"
|
|
||||||
: liberatedStatus == LiberatedState.Liberated ? "green"
|
|
||||||
: throw new Exception("Unexpected liberation state");
|
|
||||||
var image_pdf
|
|
||||||
= pdfStatus == PdfState.NoPdf ? ""
|
|
||||||
: pdfStatus == PdfState.NotDownloaded ? "_pdf_no"
|
|
||||||
: pdfStatus == PdfState.Downloaded ? "_pdf_yes"
|
|
||||||
: throw new Exception("Unexpected PDF state");
|
|
||||||
var image = (Bitmap)Properties.Resources.ResourceManager.GetObject($"liberate_{image_lib}{image_pdf}");
|
|
||||||
drawImage(e, image);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void liberate_Click(object sender, DataGridViewCellEventArgs e)
|
private async Task Liberate_Click(GridEntry liveGridEntry)
|
||||||
{
|
{
|
||||||
if (!isColumnValid(e, LIBERATE))
|
var libraryBook = liveGridEntry.LibraryBook;
|
||||||
return;
|
|
||||||
|
|
||||||
var libraryBook = getGridEntry(e.RowIndex).GetLibraryBook();
|
|
||||||
|
|
||||||
// liberated: open explorer to file
|
// liberated: open explorer to file
|
||||||
if (TransitionalFileLocator.Audio_Exists(libraryBook.Book))
|
if (TransitionalFileLocator.Audio_Exists(libraryBook.Book))
|
||||||
@ -195,146 +90,24 @@ namespace LibationWinForms
|
|||||||
// else: liberate
|
// else: liberate
|
||||||
await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(libraryBook, (_, __) => RefreshRow(libraryBook.Book.AudibleProductId));
|
await BookLiberation.ProcessorAutomationController.BackupSingleBookAsync(libraryBook, (_, __) => RefreshRow(libraryBook.Book.AudibleProductId));
|
||||||
}
|
}
|
||||||
#endregion
|
|
||||||
|
|
||||||
public void RefreshRow(string productId)
|
private void EditTags_Click(GridEntry liveGridEntry)
|
||||||
{
|
{
|
||||||
var rowId = getRowId((ge) => ge.AudibleProductId == productId);
|
var bookDetailsForm = new BookDetailsDialog(liveGridEntry.Title, liveGridEntry.Tags);
|
||||||
|
|
||||||
// update cells incl Liberate button text
|
|
||||||
dataGridView.InvalidateRow(rowId);
|
|
||||||
|
|
||||||
// needed in case filtering by -IsLiberated and it gets changed to Liberated. want to immediately show the change
|
|
||||||
filter();
|
|
||||||
|
|
||||||
BackupCountsChanged?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region tag buttons
|
|
||||||
private void addEditTagsButtons()
|
|
||||||
{
|
|
||||||
dataGridView.Columns.Add(new DataGridViewButtonColumn { HeaderText = EDIT_TAGS });
|
|
||||||
|
|
||||||
dataGridView.CellPainting += editTags_Paint;
|
|
||||||
dataGridView.CellContentClick += editTags_Click;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void editTags_Paint(object sender, DataGridViewCellPaintingEventArgs e)
|
|
||||||
{
|
|
||||||
// DataGridView Image for Button Column: https://stackoverflow.com/a/36253883
|
|
||||||
|
|
||||||
if (!isColumnValid(e, EDIT_TAGS))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var cell = getCell(e);
|
|
||||||
var gridEntry = getGridEntry(e.RowIndex);
|
|
||||||
|
|
||||||
var displayTags = gridEntry.TagsEnumerated.ToList();
|
|
||||||
|
|
||||||
if (displayTags.Any())
|
|
||||||
cell.Value = string.Join("\r\n", displayTags);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// if removing all tags: clear previous tag text
|
|
||||||
cell.Value = "";
|
|
||||||
drawImage(e, Properties.Resources.edit_tags_25x25);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void editTags_Click(object sender, DataGridViewCellEventArgs e)
|
|
||||||
{
|
|
||||||
// handle grid button click: https://stackoverflow.com/a/13687844
|
|
||||||
|
|
||||||
var dgv = (DataGridView)sender;
|
|
||||||
|
|
||||||
if (!isColumnValid(e, EDIT_TAGS))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var liveGridEntry = getGridEntry(e.RowIndex);
|
|
||||||
|
|
||||||
// EditTagsDialog should display better-formatted title
|
|
||||||
liveGridEntry.TryDisplayValue(nameof(liveGridEntry.Title), out string value);
|
|
||||||
|
|
||||||
var bookDetailsForm = new BookDetailsDialog(value, liveGridEntry.Tags);
|
|
||||||
if (bookDetailsForm.ShowDialog() != DialogResult.OK)
|
if (bookDetailsForm.ShowDialog() != DialogResult.OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var qtyChanges = LibraryCommands.UpdateTags(liveGridEntry.GetBook(), bookDetailsForm.NewTags);
|
var qtyChanges = LibraryCommands.UpdateTags(liveGridEntry.LibraryBook.Book, bookDetailsForm.NewTags);
|
||||||
if (qtyChanges == 0)
|
if (qtyChanges == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// force a re-draw, and re-apply filters
|
//Re-apply filters
|
||||||
|
Filter();
|
||||||
// needed to update text colors
|
|
||||||
dgv.InvalidateRow(e.RowIndex);
|
|
||||||
|
|
||||||
filter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private static void drawImage(DataGridViewCellPaintingEventArgs e, Bitmap image)
|
#region UI display functions
|
||||||
{
|
|
||||||
e.Paint(e.CellBounds, DataGridViewPaintParts.All);
|
|
||||||
|
|
||||||
var w = image.Width;
|
|
||||||
var h = image.Height;
|
|
||||||
var x = e.CellBounds.Left + (e.CellBounds.Width - w) / 2;
|
|
||||||
var y = e.CellBounds.Top + (e.CellBounds.Height - h) / 2;
|
|
||||||
|
|
||||||
e.Graphics.DrawImage(image, new Rectangle(x, y, w, h));
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool isColumnValid(DataGridViewCellEventArgs e, string colName) => isColumnValid(e.RowIndex, e.ColumnIndex, colName);
|
|
||||||
private bool isColumnValid(DataGridViewCellPaintingEventArgs e, string colName) => isColumnValid(e.RowIndex, e.ColumnIndex, colName);
|
|
||||||
private bool isColumnValid(int rowIndex, int colIndex, string colName)
|
|
||||||
{
|
|
||||||
var col = dataGridView.Columns[colIndex];
|
|
||||||
return rowIndex >= 0 && col.Name == colName && col is DataGridViewButtonColumn;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void formatColumns()
|
|
||||||
{
|
|
||||||
for (var i = dataGridView.ColumnCount - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
var col = dataGridView.Columns[i];
|
|
||||||
|
|
||||||
// initial HeaderText is the lookup name from GridEntry class. any formatting below won't change this
|
|
||||||
col.Name = col.HeaderText;
|
|
||||||
|
|
||||||
if (!(col is DataGridViewImageColumn || col is DataGridViewButtonColumn))
|
|
||||||
col.SortMode = DataGridViewColumnSortMode.Automatic;
|
|
||||||
|
|
||||||
col.HeaderText = col.HeaderText.Replace("_", " ");
|
|
||||||
|
|
||||||
col.Width = col.Name switch
|
|
||||||
{
|
|
||||||
LIBERATE => 70,
|
|
||||||
nameof(GridEntry.Cover) => 80,
|
|
||||||
nameof(GridEntry.Title) => col.Width * 2,
|
|
||||||
nameof(GridEntry.Misc) => (int)(col.Width * 1.35),
|
|
||||||
var n when n.In(nameof(GridEntry.My_Rating), nameof(GridEntry.Product_Rating)) => col.Width + 8,
|
|
||||||
_ => col.Width
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region live update newly downloaded and cached images
|
|
||||||
private void manageLiveImageUpdateSubscriptions()
|
|
||||||
{
|
|
||||||
FileManager.PictureStorage.PictureCached += crossThreadImageUpdate;
|
|
||||||
Disposed += (_, __) => FileManager.PictureStorage.PictureCached -= crossThreadImageUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void crossThreadImageUpdate(object _, string pictureId)
|
|
||||||
=> dataGridView.UIThread(() => updateRowImage(pictureId));
|
|
||||||
private void updateRowImage(string pictureId)
|
|
||||||
{
|
|
||||||
var rowId = getRowId((ge) => ge.PictureId == pictureId);
|
|
||||||
if (rowId > -1)
|
|
||||||
dataGridView.InvalidateRow(rowId);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
private bool hasBeenDisplayed = false;
|
private bool hasBeenDisplayed = false;
|
||||||
public void Display()
|
public void Display()
|
||||||
@ -352,15 +125,15 @@ namespace LibationWinForms
|
|||||||
// if no data. hide all columns. return
|
// if no data. hide all columns. return
|
||||||
if (!lib.Any())
|
if (!lib.Any())
|
||||||
{
|
{
|
||||||
for (var i = dataGridView.ColumnCount - 1; i >= 0; i--)
|
for (var i = _dataGridView.ColumnCount - 1; i >= 0; i--)
|
||||||
dataGridView.Columns.RemoveAt(i);
|
_dataGridView.Columns.RemoveAt(i);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var orderedGridEntries = lib
|
var orderedGridEntries = lib
|
||||||
.Select(lb => new GridEntry(lb)).ToList()
|
.Select(lb => new GridEntry(lb)).ToList()
|
||||||
// default load order
|
// default load order
|
||||||
.OrderByDescending(ge => ge.Purchase_Date)
|
.OrderByDescending(ge => ge.PurchaseDate)
|
||||||
//// more advanced example: sort by author, then series, then title
|
//// more advanced example: sort by author, then series, then title
|
||||||
//.OrderBy(ge => ge.Authors)
|
//.OrderBy(ge => ge.Authors)
|
||||||
// .ThenBy(ge => ge.Series)
|
// .ThenBy(ge => ge.Series)
|
||||||
@ -370,49 +143,83 @@ namespace LibationWinForms
|
|||||||
//
|
//
|
||||||
// BIND
|
// BIND
|
||||||
//
|
//
|
||||||
gridEntryBindingSource.DataSource = orderedGridEntries.ToSortableBindingList();
|
gridEntryBindingSource.DataSource = new SortableBindingList2<GridEntry>(orderedGridEntries);
|
||||||
|
|
||||||
//
|
//
|
||||||
// FILTER
|
// FILTER
|
||||||
//
|
//
|
||||||
filter();
|
Filter();
|
||||||
|
|
||||||
BackupCountsChanged?.Invoke(this, EventArgs.Empty);
|
BackupCountsChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RefreshRow(string productId)
|
||||||
|
{
|
||||||
|
var rowId = getRowIndex((ge) => ge.AudibleProductId == productId);
|
||||||
|
|
||||||
|
// update cells incl Liberate button text
|
||||||
|
_dataGridView.InvalidateRow(rowId);
|
||||||
|
|
||||||
|
// needed in case filtering by -IsLiberated and it gets changed to Liberated. want to immediately show the change
|
||||||
|
Filter();
|
||||||
|
|
||||||
|
BackupCountsChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region format text cells. ie: not buttons
|
||||||
|
|
||||||
|
private void HiddenFormatting(object sender, DataGridViewCellFormattingEventArgs e)
|
||||||
|
{
|
||||||
|
var dgv = (DataGridView)sender;
|
||||||
|
// no action needed for buttons
|
||||||
|
if (e.RowIndex < 0 || dgv.Columns[e.ColumnIndex] is DataGridViewButtonColumn)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var isHidden = getGridEntry(e.RowIndex).TagsEnumerated.Contains("hidden");
|
||||||
|
|
||||||
|
getCell(e).Style
|
||||||
|
= isHidden
|
||||||
|
? new DataGridViewCellStyle { ForeColor = Color.LightGray }
|
||||||
|
: dgv.DefaultCellStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region filter
|
#region filter
|
||||||
|
|
||||||
string _filterSearchString;
|
string _filterSearchString;
|
||||||
private void filter() => Filter(_filterSearchString);
|
private void Filter() => Filter(_filterSearchString);
|
||||||
public void Filter(string searchString)
|
public void Filter(string searchString)
|
||||||
{
|
{
|
||||||
_filterSearchString = searchString;
|
_filterSearchString = searchString;
|
||||||
|
|
||||||
if (dataGridView.Rows.Count == 0)
|
if (_dataGridView.Rows.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var searchResults = SearchEngineCommands.Search(searchString);
|
var searchResults = SearchEngineCommands.Search(searchString);
|
||||||
var productIds = searchResults.Docs.Select(d => d.ProductId).ToList();
|
var productIds = searchResults.Docs.Select(d => d.ProductId).ToList();
|
||||||
|
|
||||||
// https://stackoverflow.com/a/18942430
|
// https://stackoverflow.com/a/18942430
|
||||||
var currencyManager = (CurrencyManager)BindingContext[dataGridView.DataSource];
|
var currencyManager = (CurrencyManager)BindingContext[_dataGridView.DataSource];
|
||||||
currencyManager.SuspendBinding();
|
currencyManager.SuspendBinding();
|
||||||
{
|
{
|
||||||
for (var r = dataGridView.RowCount - 1; r >= 0; r--)
|
for (var r = _dataGridView.RowCount - 1; r >= 0; r--)
|
||||||
dataGridView.Rows[r].Visible = productIds.Contains(getGridEntry(r).AudibleProductId);
|
_dataGridView.Rows[r].Visible = productIds.Contains(getGridEntry(r).AudibleProductId);
|
||||||
}
|
}
|
||||||
currencyManager.ResumeBinding();
|
currencyManager.ResumeBinding();
|
||||||
VisibleCountChanged?.Invoke(this, dataGridView.AsEnumerable().Count(r => r.Visible));
|
VisibleCountChanged?.Invoke(this, _dataGridView.AsEnumerable().Count(r => r.Visible));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
private int getRowId(Func<GridEntry, bool> func) => dataGridView.GetRowIdOfBoundItem(func);
|
#region DataGridView Macros
|
||||||
|
|
||||||
private GridEntry getGridEntry(int rowIndex) => dataGridView.GetBoundItem<GridEntry>(rowIndex);
|
private int getRowIndex(Func<GridEntry, bool> func) => _dataGridView.GetRowIdOfBoundItem(func);
|
||||||
|
private GridEntry getGridEntry(int rowIndex) => _dataGridView.GetBoundItem<GridEntry>(rowIndex);
|
||||||
|
private DataGridViewCell getCell(DataGridViewCellFormattingEventArgs e) => _dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex];
|
||||||
|
|
||||||
private DataGridViewCell getCell(DataGridViewCellFormattingEventArgs e) => getCell(e.RowIndex, e.ColumnIndex);
|
#endregion
|
||||||
|
|
||||||
private DataGridViewCell getCell(DataGridViewCellPaintingEventArgs e) => getCell(e.RowIndex, e.ColumnIndex);
|
|
||||||
|
|
||||||
private DataGridViewCell getCell(int rowIndex, int columnIndex) => dataGridView.Rows[rowIndex].Cells[columnIndex];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,64 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<root>
|
||||||
<root>
|
|
||||||
<!--
|
|
||||||
Microsoft ResX Schema
|
|
||||||
|
|
||||||
Version 2.0
|
|
||||||
|
|
||||||
The primary goals of this format is to allow a simple XML format
|
|
||||||
that is mostly human readable. The generation and parsing of the
|
|
||||||
various data types are done through the TypeConverter classes
|
|
||||||
associated with the data types.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
... ado.net/XML headers & schema ...
|
|
||||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
|
||||||
<resheader name="version">2.0</resheader>
|
|
||||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
|
||||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
|
||||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
|
||||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
|
||||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
|
||||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
|
||||||
</data>
|
|
||||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
|
||||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
|
||||||
<comment>This is a comment</comment>
|
|
||||||
</data>
|
|
||||||
|
|
||||||
There are any number of "resheader" rows that contain simple
|
|
||||||
name/value pairs.
|
|
||||||
|
|
||||||
Each data row contains a name, and value. The row also contains a
|
|
||||||
type or mimetype. Type corresponds to a .NET class that support
|
|
||||||
text/value conversion through the TypeConverter architecture.
|
|
||||||
Classes that don't support this are serialized and stored with the
|
|
||||||
mimetype set.
|
|
||||||
|
|
||||||
The mimetype is used for serialized objects, and tells the
|
|
||||||
ResXResourceReader how to depersist the object. This is currently not
|
|
||||||
extensible. For a given mimetype the value must be set accordingly:
|
|
||||||
|
|
||||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
|
||||||
that the ResXResourceWriter will generate, however the reader can
|
|
||||||
read any of the formats listed below.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.binary.base64
|
|
||||||
value : The object must be serialized with
|
|
||||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.soap.base64
|
|
||||||
value : The object must be serialized with
|
|
||||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
|
|
||||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
|
||||||
value : The object must be serialized into a byte array
|
|
||||||
: using a System.ComponentModel.TypeConverter
|
|
||||||
: and then encoded with base64 encoding.
|
|
||||||
-->
|
|
||||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
@ -120,4 +60,19 @@
|
|||||||
<metadata name="gridEntryBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
<metadata name="gridEntryBindingSource.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
<value>17, 17</value>
|
<value>17, 17</value>
|
||||||
</metadata>
|
</metadata>
|
||||||
|
<metadata name="dataGridViewImageButtonBoxColumn1.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="ProductRating.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="PurchaseDate.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="MyRating.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
|
<metadata name="dataGridViewImageButtonBoxColumn2.UserAddedColumn" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||||
|
<value>True</value>
|
||||||
|
</metadata>
|
||||||
</root>
|
</root>
|
||||||
72
LibationWinForms/SortableBindingList2[T].cs
Normal file
72
LibationWinForms/SortableBindingList2[T].cs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace LibationWinForms
|
||||||
|
{
|
||||||
|
class SortableBindingList2<T> : BindingList<T> where T : IObjectMemberComparable
|
||||||
|
{
|
||||||
|
private ObjectMemberComparer<T> Comparer = new();
|
||||||
|
|
||||||
|
private bool isSorted;
|
||||||
|
private ListSortDirection listSortDirection;
|
||||||
|
private PropertyDescriptor propertyDescriptor;
|
||||||
|
|
||||||
|
public SortableBindingList2() : base(new List<T>()) { }
|
||||||
|
|
||||||
|
public SortableBindingList2(IEnumerable<T> enumeration) : base(new List<T>(enumeration)) { }
|
||||||
|
|
||||||
|
protected override bool SupportsSortingCore => true;
|
||||||
|
|
||||||
|
protected override bool IsSortedCore => isSorted;
|
||||||
|
|
||||||
|
protected override PropertyDescriptor SortPropertyCore => propertyDescriptor;
|
||||||
|
|
||||||
|
protected override ListSortDirection SortDirectionCore => listSortDirection;
|
||||||
|
|
||||||
|
protected override bool SupportsSearchingCore => true;
|
||||||
|
|
||||||
|
protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
|
||||||
|
{
|
||||||
|
List<T> itemsList = (List<T>)Items;
|
||||||
|
|
||||||
|
Comparer.PropertyName = property.Name;
|
||||||
|
Comparer.Direction = direction;
|
||||||
|
|
||||||
|
itemsList.Sort(Comparer);
|
||||||
|
|
||||||
|
propertyDescriptor = property;
|
||||||
|
listSortDirection = direction;
|
||||||
|
isSorted = true;
|
||||||
|
|
||||||
|
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void RemoveSortCore()
|
||||||
|
{
|
||||||
|
isSorted = false;
|
||||||
|
propertyDescriptor = base.SortPropertyCore;
|
||||||
|
listSortDirection = base.SortDirectionCore;
|
||||||
|
|
||||||
|
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override int FindCore(PropertyDescriptor property, object key)
|
||||||
|
{
|
||||||
|
int count = Count;
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
T element = this[i];
|
||||||
|
if (property.GetValue(element).Equals(key))
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user