diff --git a/LibationLauncher/LibationLauncher.csproj b/LibationLauncher/LibationLauncher.csproj index 5af2cd25..33159e83 100644 --- a/LibationLauncher/LibationLauncher.csproj +++ b/LibationLauncher/LibationLauncher.csproj @@ -13,7 +13,11 @@ win-x64 - 5.4.9.79 + 5.4.9.120 + + + + TRACE;DEBUG diff --git a/LibationLauncher/Program.cs b/LibationLauncher/Program.cs index 2fa3c87a..ab4c6e1c 100644 --- a/LibationLauncher/Program.cs +++ b/LibationLauncher/Program.cs @@ -59,7 +59,10 @@ namespace LibationLauncher ensureSerilogConfig(config); configureLogging(config); logStartupState(config); + +#if !DEBUG checkForUpdate(config); +#endif Application.Run(new Form1()); } @@ -145,7 +148,7 @@ namespace LibationLauncher CancelInstallation(); } - #region migrate to v5.0.0 re-register device if device info not in settings +#region migrate to v5.0.0 re-register device if device info not in settings private static void migrate_to_v5_0_0(Configuration config) { if (!config.Exists(nameof(config.AllowLibationFixup))) @@ -187,9 +190,9 @@ namespace LibationLauncher } } } - #endregion +#endregion - #region migrate to v5.2.0 +#region migrate to v5.2.0 // get rid of meta-directories, combine DownloadsInProgressEnum and DecryptInProgressEnum => InProgress private static void migrate_to_v5_2_0__pre_config() { @@ -231,9 +234,9 @@ namespace LibationLauncher if (!config.Exists(nameof(config.DecryptToLossy))) config.DecryptToLossy = false; } - #endregion +#endregion - #region migrate to v5.4.1 see comment +#region migrate to v5.4.1 see comment // this 'migration' is a bit different. it intentionally runs each time Libation is started. its job will be fulfilled when I eventually // implement the portion which removes FilePaths.json, at which time this method will be a proper migration // @@ -297,7 +300,7 @@ namespace LibationLauncher debugStopwatch.Stop(); var debugTotal = debugStopwatch.Elapsed; } - #endregion +#endregion private static void ensureSerilogConfig(Configuration config) { @@ -418,6 +421,7 @@ namespace LibationLauncher if (latest is null) return; + var latestVersionString = latest.TagName.Trim('v'); if (!Version.TryParse(latestVersionString, out var latestRelease)) return; diff --git a/LibationWinForms/Dialogs/RemoveBooksDialog.cs b/LibationWinForms/Dialogs/RemoveBooksDialog.cs index 7bfafb8c..04e7af16 100644 --- a/LibationWinForms/Dialogs/RemoveBooksDialog.cs +++ b/LibationWinForms/Dialogs/RemoveBooksDialog.cs @@ -39,8 +39,8 @@ namespace LibationWinForms.Dialogs dataGridView1.BindingContextChanged += (s, e) => UpdateSelection(); var orderedGridEntries = _libraryBooks - .Select(lb => new RemovableGridEntry(new GridEntry(lb))) - .OrderByDescending(ge => ge.GridEntry.PurchaseDate) + .Select(lb => new RemovableGridEntry(lb)) + .OrderByDescending(ge => ge.PurchaseDate) .ToList(); _removableGridEntries = orderedGridEntries.ToSortableBindingList(); @@ -65,7 +65,7 @@ namespace LibationWinForms.Dialogs { var rmovedBooks = await LibraryCommands.FindInactiveBooks((account) => new WinformResponder(account), _libraryBooks, _accounts); - var removable = _removableGridEntries.Where(rge => rmovedBooks.Count(rb => rb.Book.AudibleProductId == rge.GridEntry.AudibleProductId) == 1); + var removable = _removableGridEntries.Where(rge => rmovedBooks.Count(rb => rb.Book.AudibleProductId == rge.AudibleProductId) == 1); if (removable.Count() == 0) return; @@ -110,7 +110,7 @@ namespace LibationWinForms.Dialogs var libBooks = context.GetLibrary_Flat_NoTracking(); - var removeLibraryBooks = libBooks.Where(lb => selected.Count(rge => rge.GridEntry.AudibleProductId == lb.Book.AudibleProductId) == 1).ToArray(); + var removeLibraryBooks = libBooks.Where(lb => selected.Count(rge => rge.AudibleProductId == lb.Book.AudibleProductId) == 1).ToArray(); context.Library.RemoveRange(removeLibraryBooks); context.SaveChanges(); BooksRemoved = true; @@ -141,11 +141,8 @@ namespace LibationWinForms.Dialogs } - internal class RemovableGridEntry : INotifyPropertyChanged + internal class RemovableGridEntry : GridEntry { - public event PropertyChangedEventHandler PropertyChanged; - public GridEntry GridEntry { get; } - public bool Remove { get @@ -160,51 +157,12 @@ namespace LibationWinForms.Dialogs NotifyPropertyChanged(); } } - } - public Image Cover - { - get - { - return _cover; - } - set - { - _cover = value; - NotifyPropertyChanged(); - } - } - public string Title => GridEntry.Title; - public string Authors => GridEntry.Authors; - public string Misc => GridEntry.Misc; - public string DatePurchased => GridEntry.PurchaseDate; + } private bool _remove = false; - private Image _cover; - public RemovableGridEntry(GridEntry gridEntry) + public RemovableGridEntry(LibraryBook libraryBook) :base(libraryBook) { - GridEntry = gridEntry; - - var picDef = new FileManager.PictureDefinition(GridEntry.LibraryBook.Book.PictureId, FileManager.PictureSize._80x80); - (bool isDefault, byte[] picture) = FileManager.PictureStorage.GetPicture(picDef); - - if (isDefault) - FileManager.PictureStorage.PictureCached += PictureStorage_PictureCached; - - _cover = ImageReader.ToImage(picture); } - - private void PictureStorage_PictureCached(object sender, string pictureId) - { - if (pictureId == GridEntry.LibraryBook.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)); - } } diff --git a/LibationWinForms/EditTagsDataGridViewImageButtonColumn.cs b/LibationWinForms/EditTagsDataGridViewImageButtonColumn.cs index fdee638f..58f39545 100644 --- a/LibationWinForms/EditTagsDataGridViewImageButtonColumn.cs +++ b/LibationWinForms/EditTagsDataGridViewImageButtonColumn.cs @@ -12,15 +12,23 @@ namespace LibationWinForms internal class EditTagsDataGridViewImageButtonCell : DataGridViewImageButtonCell { private static readonly Bitmap ButtonImage = Properties.Resources.edit_tags_25x25; + private static readonly Color HiddenForeColor = Color.LightGray; + 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) + var valueString = (string)value; + + DataGridView.Rows[RowIndex].DefaultCellStyle.ForeColor = valueString?.Contains("hidden") == true ? HiddenForeColor : DataGridView.DefaultCellStyle.ForeColor; + + if (valueString.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); + } } } } diff --git a/LibationWinForms/Form1.cs b/LibationWinForms/Form1.cs index 4221082c..56d44638 100644 --- a/LibationWinForms/Form1.cs +++ b/LibationWinForms/Form1.cs @@ -48,13 +48,6 @@ namespace LibationWinForms this.Load += (_, __) => RestoreSizeAndLocation(); this.Load += (_, __) => RefreshImportMenu(); - // start background service - this.Load += (_, __) => startBackgroundImageDownloader(); - } - - private static void startBackgroundImageDownloader() - { - // load default/missing cover images. this will also initiate the background image downloader var format = System.Drawing.Imaging.ImageFormat.Jpeg; PictureStorage.SetDefaultImage(PictureSize._80x80, Properties.Resources.default_cover_80x80.ToBytes(format)); PictureStorage.SetDefaultImage(PictureSize._300x300, Properties.Resources.default_cover_300x300.ToBytes(format)); diff --git a/LibationWinForms/GridEntry.cs b/LibationWinForms/GridEntry.cs index d20c6553..f9de6708 100644 --- a/LibationWinForms/GridEntry.cs +++ b/LibationWinForms/GridEntry.cs @@ -5,9 +5,11 @@ using System.ComponentModel; using System.Drawing; using System.Linq; using System.Runtime.CompilerServices; +using System.Threading; using ApplicationServices; using DataLayer; using Dinah.Core.Drawing; +using Dinah.Core.Windows.Forms; namespace LibationWinForms { @@ -32,6 +34,7 @@ namespace LibationWinForms public event PropertyChangedEventHandler PropertyChanged; private Book Book => LibraryBook.Book; + private SynchronizationContext SyncContext { get; } = SynchronizationContext.Current; private Image _cover; public GridEntry(LibraryBook libraryBook) @@ -72,16 +75,23 @@ namespace LibationWinForms { if (pictureId == Book.PictureId) { + //GridEntry SHOULD be UI-ignorant, but PropertyChanged 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)); + protected void NotifyPropertyChanged([CallerMemberName] string propertyName = "") + => SyncContext.Post( + args => OnPropertyChangedAsync(args as AsyncCompletedEventArgs), + new AsyncCompletedEventArgs(null, false, new PropertyChangedEventArgs(propertyName)) + ); - #region Data Source properties - public Image Cover + private void OnPropertyChangedAsync(AsyncCompletedEventArgs e) => + PropertyChanged?.Invoke(this, e.UserState as PropertyChangedEventArgs); + + #region Data Source properties + public Image Cover { get { diff --git a/LibationWinForms/ProductsGrid.cs b/LibationWinForms/ProductsGrid.cs index f06b8c1a..3f02b2e6 100644 --- a/LibationWinForms/ProductsGrid.cs +++ b/LibationWinForms/ProductsGrid.cs @@ -1,5 +1,4 @@ using System; -using System.Drawing; using System.Linq; using System.Threading.Tasks; using System.Windows.Forms; @@ -38,7 +37,6 @@ namespace LibationWinForms // sorting breaks filters. must reapply filters after sorting _dataGridView.Sorted += (_, __) => Filter(); - _dataGridView.CellFormatting += HiddenFormatting; _dataGridView.CellContentClick += DataGridView_CellContentClick; EnableDoubleBuffering(); @@ -133,7 +131,7 @@ namespace LibationWinForms var orderedGridEntries = lib .Select(lb => new GridEntry(lb)).ToList() // default load order - .OrderByDescending(ge => ge.PurchaseDate) + .OrderByDescending(ge => (DateTime)ge.GetMemberValue(nameof(ge.PurchaseDate))) //// more advanced example: sort by author, then series, then title //.OrderBy(ge => ge.Authors) // .ThenBy(ge => ge.Series) @@ -155,10 +153,10 @@ namespace LibationWinForms public void RefreshRow(string productId) { - var rowId = getRowIndex((ge) => ge.AudibleProductId == productId); + var rowIndex = getRowIndex((ge) => ge.AudibleProductId == productId); // update cells incl Liberate button text - _dataGridView.InvalidateRow(rowId); + _dataGridView.InvalidateRow(rowIndex); // needed in case filtering by -IsLiberated and it gets changed to Liberated. want to immediately show the change Filter(); @@ -166,28 +164,9 @@ namespace LibationWinForms 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; private void Filter() => Filter(_filterSearchString); @@ -220,7 +199,6 @@ namespace LibationWinForms private int getRowIndex(Func func) => _dataGridView.GetRowIdOfBoundItem(func); private GridEntry getGridEntry(int rowIndex) => _dataGridView.GetBoundItem(rowIndex); - private DataGridViewCell getCell(DataGridViewCellFormattingEventArgs e) => _dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex]; #endregion }