From e65b6c76a8d27b4286c03d23e293f4d4b78fd6fa Mon Sep 17 00:00:00 2001 From: MBucari Date: Sat, 12 Aug 2023 17:12:38 -0600 Subject: [PATCH] Add ability to preview templates on user's books (#700) Add template editor menu items to main grid context menu --- Source/FileLiberator/AudioFileStorageExt.cs | 3 +- .../Views/ProductsDisplay.axaml.cs | 223 ++++++++++-------- .../LibationFileManager/TemplateEditor[T].cs | 25 +- .../GridView/GridContextMenu.cs | 114 +++++++++ .../GridView/ProductsDisplay.cs | 104 ++++---- 5 files changed, 306 insertions(+), 163 deletions(-) create mode 100644 Source/LibationUiBase/GridView/GridContextMenu.cs diff --git a/Source/FileLiberator/AudioFileStorageExt.cs b/Source/FileLiberator/AudioFileStorageExt.cs index e5c2e344..b2eda7cf 100644 --- a/Source/FileLiberator/AudioFileStorageExt.cs +++ b/Source/FileLiberator/AudioFileStorageExt.cs @@ -21,7 +21,8 @@ namespace FileLiberator var series = libraryBook.Book.SeriesLink.SingleOrDefault(); if (series is not null) { - var seriesParent = ApplicationServices.DbContexts.GetContext().GetLibraryBook_Flat_NoTracking(series.Series.AudibleSeriesId); + using var context = ApplicationServices.DbContexts.GetContext(); + var seriesParent = context.GetLibraryBook_Flat_NoTracking(series.Series.AudibleSeriesId); if (seriesParent is not null) { diff --git a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs index 4ba3516d..6f6948ab 100644 --- a/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs +++ b/Source/LibationAvalonia/Views/ProductsDisplay.axaml.cs @@ -11,6 +11,7 @@ using LibationAvalonia.Dialogs; using LibationAvalonia.ViewModels; using LibationFileManager; using LibationUiBase.GridView; +using ReactiveUI; using System; using System.Collections.Generic; using System.Linq; @@ -172,126 +173,107 @@ namespace LibationAvalonia.Views public void ProductsGrid_CellContextMenuStripNeeded(object sender, DataGridCellContextMenuStripNeededEventArgs args) { + var entry = args.GridEntry; + var ctx = new GridContextMenu(entry, '_'); + if (args.Column.SortMemberPath is not "Liberate" and not "Cover") { - var menuItem = new MenuItem { Header = "_Copy Cell Contents" }; - - menuItem.Click += async (s, e) - => await App.MainWindow.Clipboard.SetTextAsync(args.CellClipboardContents); - - args.ContextMenuItems.Add(menuItem); + args.ContextMenuItems.Add(new MenuItem + { + Header = ctx.CopyCellText, + Command = ReactiveCommand.CreateFromTask(() => App.MainWindow.Clipboard.SetTextAsync(args.CellClipboardContents)) + }); args.ContextMenuItems.Add(new Separator()); } - var entry = args.GridEntry; #region Liberate all Episodes if (entry.Liberate.IsSeries) { - var liberateEpisodesMenuItem = new MenuItem() + args.ContextMenuItems.Add(new MenuItem() { - Header = "_Liberate All Episodes", - IsEnabled = ((ISeriesEntry)entry).Children.Any(c => c.Liberate.BookStatus is LiberatedStatus.NotLiberated or LiberatedStatus.PartialDownload) - }; - - args.ContextMenuItems.Add(liberateEpisodesMenuItem); - - liberateEpisodesMenuItem.Click += (_, _) => LiberateSeriesClicked?.Invoke(this, ((ISeriesEntry)entry)); + Header = ctx.LiberateEpisodesText, + IsEnabled = ctx.LiberateEpisodesEnabled, + Command = ReactiveCommand.Create(() => LiberateSeriesClicked?.Invoke(this, (ISeriesEntry)entry)) + }); } #endregion #region Set Download status to Downloaded - var setDownloadMenuItem = new MenuItem() + args.ContextMenuItems.Add(new MenuItem() { - Header = "Set Download status to '_Downloaded'", - IsEnabled = entry.Book.UserDefinedItem.BookStatus != LiberatedStatus.Liberated || entry.Liberate.IsSeries - }; - - args.ContextMenuItems.Add(setDownloadMenuItem); - - if (entry.Liberate.IsSeries) - setDownloadMenuItem.Click += (_, __) => ((ISeriesEntry)entry).Children.Select(c => c.LibraryBook).UpdateBookStatus(LiberatedStatus.Liberated); - else - setDownloadMenuItem.Click += (_, __) => entry.LibraryBook.UpdateBookStatus(LiberatedStatus.Liberated); + Header = ctx.SetDownloadedText, + IsEnabled = ctx.SetDownloadedEnabled, + Command = ReactiveCommand.Create(ctx.SetDownloaded) + }); #endregion #region Set Download status to Not Downloaded - var setNotDownloadMenuItem = new MenuItem() + args.ContextMenuItems.Add(new MenuItem() { - Header = "Set Download status to '_Not Downloaded'", - IsEnabled = entry.Book.UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated || entry.Liberate.IsSeries - }; - - args.ContextMenuItems.Add(setNotDownloadMenuItem); - - if (entry.Liberate.IsSeries) - setNotDownloadMenuItem.Click += (_, __) => ((ISeriesEntry)entry).Children.Select(c => c.LibraryBook).UpdateBookStatus(LiberatedStatus.NotLiberated); - else - setNotDownloadMenuItem.Click += (_, __) => entry.LibraryBook.UpdateBookStatus(LiberatedStatus.NotLiberated); + Header = ctx.SetNotDownloadedText, + IsEnabled = ctx.SetNotDownloadedEnabled, + Command = ReactiveCommand.Create(ctx.SetNotDownloaded) + }); #endregion #region Remove from library - var removeMenuItem = new MenuItem() { Header = "_Remove from library" }; - - args.ContextMenuItems.Add(removeMenuItem); - - if (entry.Liberate.IsSeries) - removeMenuItem.Click += async (_, __) => await ((ISeriesEntry)entry).Children.Select(c => c.LibraryBook).RemoveBooksAsync(); - else - removeMenuItem.Click += async (_, __) => await Task.Run(entry.LibraryBook.RemoveBook); + args.ContextMenuItems.Add(new MenuItem + { + Header = ctx.RemoveText, + Command = ReactiveCommand.CreateFromTask(ctx.RemoveAsync) + }); #endregion if (!entry.Liberate.IsSeries) { #region Locate file - var locateFileMenuItem = new MenuItem() { Header = "_Locate file..." }; - args.ContextMenuItems.Add(locateFileMenuItem); - - locateFileMenuItem.Click += async (_, __) => + args.ContextMenuItems.Add(new MenuItem { - try + Header = ctx.LocateFileText, + Command = ReactiveCommand.CreateFromTask(async () => { - var window = this.GetParentWindow(); - - var openFileDialogOptions = new FilePickerOpenOptions + try { - Title = $"Locate the audio file for '{entry.Book.TitleWithSubtitle}'", - AllowMultiple = false, - SuggestedStartLocation = await window.StorageProvider.TryGetFolderFromPathAsync(Configuration.Instance.Books.PathWithoutPrefix), - FileTypeFilter = new FilePickerFileType[] + var window = this.GetParentWindow(); + + var openFileDialogOptions = new FilePickerOpenOptions { - new("All files (*.*)") { Patterns = new[] { "*" } }, - } - }; + Title = ctx.LocateFileDialogTitle, + AllowMultiple = false, + SuggestedStartLocation = await window.StorageProvider.TryGetFolderFromPathAsync(Configuration.Instance.Books.PathWithoutPrefix), + FileTypeFilter = new FilePickerFileType[] + { + new("All files (*.*)") { Patterns = new[] { "*" } }, + } + }; - var selectedFiles = await window.StorageProvider.OpenFilePickerAsync(openFileDialogOptions); - var selectedFile = selectedFiles.SingleOrDefault()?.TryGetLocalPath(); + var selectedFiles = await window.StorageProvider.OpenFilePickerAsync(openFileDialogOptions); + var selectedFile = selectedFiles.SingleOrDefault()?.TryGetLocalPath(); - if (selectedFile is not null) - FilePathCache.Insert(entry.AudibleProductId, selectedFile); - } - catch (Exception ex) - { - var msg = "Error saving book's location"; - await MessageBox.ShowAdminAlert(null, msg, msg, ex); - } - }; + if (selectedFile is not null) + FilePathCache.Insert(entry.AudibleProductId, selectedFile); + } + catch (Exception ex) + { + await MessageBox.ShowAdminAlert(null, ctx.LocateFileErrorMessage, ctx.LocateFileErrorMessage, ex); + } + }) + }); #endregion #region Convert to Mp3 - var convertToMp3MenuItem = new MenuItem + args.ContextMenuItems.Add(new MenuItem { - Header = "_Convert to Mp3", - IsEnabled = entry.Book.UserDefinedItem.BookStatus is LiberatedStatus.Liberated - }; - args.ContextMenuItems.Add(convertToMp3MenuItem); - - convertToMp3MenuItem.Click += (_, _) => ConvertToMp3Clicked?.Invoke(this, entry.LibraryBook); + Header = ctx.ConvertToMp3Text, + IsEnabled = ctx.ConvertToMp3Enabled, + Command = ReactiveCommand.Create(() => ConvertToMp3Clicked?.Invoke(this, entry.LibraryBook)) + }); #endregion } @@ -299,34 +281,72 @@ namespace LibationAvalonia.Views #region Force Re-Download if (!entry.Liberate.IsSeries) { - var reDownloadMenuItem = new MenuItem() + args.ContextMenuItems.Add(new MenuItem() { - Header = "Re-download this audiobook", - IsEnabled = entry.Book.UserDefinedItem.BookStatus is LiberatedStatus.Liberated - }; - - args.ContextMenuItems.Add(reDownloadMenuItem); - reDownloadMenuItem.Click += (s, _) => - { - //No need to persist this change. It only needs to last long for the file to start downloading - entry.Book.UserDefinedItem.BookStatus = LiberatedStatus.NotLiberated; - LiberateClicked?.Invoke(s, entry.LibraryBook); - }; + Header = ctx.ReDownloadText, + IsEnabled = ctx.ReDownloadEnabled, + Command = ReactiveCommand.Create(() => + { + //No need to persist this change. It only needs to last long for the file to start downloading + entry.Book.UserDefinedItem.BookStatus = LiberatedStatus.NotLiberated; + LiberateClicked?.Invoke(this, entry.LibraryBook); + }) + }); } #endregion args.ContextMenuItems.Add(new Separator()); + #region Edit Templates + async Task editTemplate(LibraryBook libraryBook, string existingTemplate, Action setNewTemplate) + where T : Templates, LibationFileManager.ITemplate, new() + { + var template = ctx.CreateTemplateEditor(libraryBook, existingTemplate); + var form = new EditTemplateDialog(template); + if (await form.ShowDialog(this.GetParentWindow()) == DialogResult.OK) + { + setNewTemplate(template.EditingTemplate.TemplateText); + } + } + + if (!entry.Liberate.IsSeries) + { + args.ContextMenuItems.Add(new MenuItem + { + Header = ctx.EditTemplatesText, + ItemsSource = new[] + { + new MenuItem + { + Header = ctx.FolderTemplateText, + Command = ReactiveCommand.CreateFromTask(() => editTemplate(entry.LibraryBook, Configuration.Instance.FolderTemplate, t => Configuration.Instance.FolderTemplate = t)) + }, + new MenuItem + { + Header = ctx.FileTemplateText, + Command = ReactiveCommand.CreateFromTask(() => editTemplate(entry.LibraryBook, Configuration.Instance.FileTemplate, t => Configuration.Instance.FileTemplate = t)) + }, + new MenuItem + { + Header = ctx.MultipartTemplateText, + Command = ReactiveCommand.CreateFromTask(() => editTemplate(entry.LibraryBook, Configuration.Instance.ChapterFileTemplate, t => Configuration.Instance.ChapterFileTemplate = t)) + } + } + }); + args.ContextMenuItems.Add(new Separator()); + } + + #endregion + #region View Bookmarks/Clips if (!entry.Liberate.IsSeries) { - - var bookRecordMenuItem = new MenuItem { Header = "View _Bookmarks/Clips" }; - - args.ContextMenuItems.Add(bookRecordMenuItem); - - bookRecordMenuItem.Click += async (_, _) => await new BookRecordsDialog(entry.LibraryBook).ShowDialog(VisualRoot as Window); + args.ContextMenuItems.Add(new MenuItem + { + Header = ctx.ViewBookmarksText, + Command = ReactiveCommand.CreateFromTask(() => new BookRecordsDialog(entry.LibraryBook).ShowDialog(VisualRoot as Window)) + }); } #endregion @@ -334,12 +354,11 @@ namespace LibationAvalonia.Views if (entry.Book.SeriesLink.Any()) { - var header = entry.Liberate.IsSeries ? "View All Episodes in Series" : "View All Books in Series"; - var viewSeriesMenuItem = new MenuItem { Header = header }; - - args.ContextMenuItems.Add(viewSeriesMenuItem); - - viewSeriesMenuItem.Click += (_, _) => new SeriesViewDialog(entry.LibraryBook).Show(); + args.ContextMenuItems.Add(new MenuItem + { + Header = ctx.ViewSeriesText, + Command = ReactiveCommand.Create(() => new SeriesViewDialog(entry.LibraryBook).Show()) + }); } #endregion diff --git a/Source/LibationFileManager/TemplateEditor[T].cs b/Source/LibationFileManager/TemplateEditor[T].cs index d8c3fea3..c30b6e59 100644 --- a/Source/LibationFileManager/TemplateEditor[T].cs +++ b/Source/LibationFileManager/TemplateEditor[T].cs @@ -51,7 +51,10 @@ namespace LibationFileManager return false; } - private static readonly LibraryBookDto libraryBookDto + public LibraryBookDto FolderBook { get; } + public LibraryBookDto LibraryBook { get; } + + private static readonly LibraryBookDto DefaultLibraryBook = new() { Account = "myaccount@example.co", @@ -74,7 +77,7 @@ namespace LibationFileManager Language = "English" }; - private static readonly MultiConvertFileProperties partFileProperties + private static readonly MultiConvertFileProperties DefaultMultipartProperties = new() { OutputFileName = "", @@ -91,23 +94,27 @@ namespace LibationFileManager * subdirectories. Without rooting, we won't be allowed to create a * relative path longer than MAX_PATH. */ - var dir = Folder?.GetFilename(libraryBookDto, BaseDirectory, ""); + var dir = Folder?.GetFilename(FolderBook, BaseDirectory, ""); if (dir is null) return null; return Path.GetRelativePath(BaseDirectory, dir); } public string? GetFileName() - => File?.GetFilename(libraryBookDto, partFileProperties, "", ""); + => File?.GetFilename(LibraryBook, DefaultMultipartProperties, "", ""); public string? GetName() - => Name?.GetName(libraryBookDto, partFileProperties); + => Name?.GetName(LibraryBook, DefaultMultipartProperties); private TemplateEditor( + LibraryBookDto? folderDto, + LibraryBookDto? fileDto, Templates editingTemplate, LongPath baseDirectory, string defaultTemplate, string templateName, string templateDescription) { + FolderBook = folderDto ?? DefaultLibraryBook; + LibraryBook = fileDto ?? DefaultLibraryBook; _editingTemplate = editingTemplate; BaseDirectory = baseDirectory; DefaultTemplate = defaultTemplate; @@ -115,12 +122,12 @@ namespace LibationFileManager TemplateDescription = templateDescription; } - public static ITemplateEditor CreateFilenameEditor(LongPath baseDir, string templateText) + public static ITemplateEditor CreateFilenameEditor(LongPath baseDir, string templateText, LibraryBookDto? folderDto = null, LibraryBookDto? fileDto = null) { if (!Templates.TryGetTemplate(templateText, out var template)) throw new ArgumentException($"Failed to parse {nameof(templateText)}"); - var templateEditor = new TemplateEditor(template, baseDir, T.DefaultTemplate, T.Name, T.Description); + var templateEditor = new TemplateEditor(folderDto, fileDto, template, baseDir, T.DefaultTemplate, T.Name, T.Description); if (!templateEditor.IsFolder && !templateEditor.IsFilePath) throw new InvalidOperationException($"This method is only for File and Folder templates. Use {nameof(CreateNameEditor)} for name templates"); @@ -133,12 +140,12 @@ namespace LibationFileManager return templateEditor; } - public static ITemplateEditor CreateNameEditor(string templateText) + public static ITemplateEditor CreateNameEditor(string templateText, LibraryBookDto? libraryBookDto = null) { if (!Templates.TryGetTemplate(templateText, out var nameTemplate)) throw new ArgumentException($"Failed to parse {nameof(templateText)}"); - var templateEditor = new TemplateEditor(nameTemplate, "", T.DefaultTemplate, T.Name, T.Description); + var templateEditor = new TemplateEditor(null, libraryBookDto, nameTemplate, "", T.DefaultTemplate, T.Name, T.Description); if (templateEditor.IsFolder || templateEditor.IsFilePath) throw new InvalidOperationException($"This method is only for name templates. Use {nameof(CreateFilenameEditor)} for file templates"); diff --git a/Source/LibationUiBase/GridView/GridContextMenu.cs b/Source/LibationUiBase/GridView/GridContextMenu.cs new file mode 100644 index 00000000..15b0f099 --- /dev/null +++ b/Source/LibationUiBase/GridView/GridContextMenu.cs @@ -0,0 +1,114 @@ +using ApplicationServices; +using DataLayer; +using FileLiberator; +using LibationFileManager; +using System; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Input; + +namespace LibationUiBase.GridView; + +public class GridContextMenu +{ + public string CopyCellText => $"{Accelerator}Copy Cell Contents"; + public string LiberateEpisodesText => $"{Accelerator}Liberate All Episodes"; + public string SetDownloadedText => $"Set Download status to '{Accelerator}Downloaded'"; + public string SetNotDownloadedText => $"Set Download status to '{Accelerator}Not Downloaded'"; + public string RemoveText => $"{Accelerator}Remove from library"; + public string LocateFileText => $"{Accelerator}Locate file..."; + public string LocateFileDialogTitle => $"Locate the audio file for '{GridEntry.Book.TitleWithSubtitle}'"; + public string LocateFileErrorMessage => "Error saving book's location"; + public string ConvertToMp3Text => $"{Accelerator}Convert to Mp3"; + public string ReDownloadText => "Re-download this audiobook"; + public string EditTemplatesText => "Edit Templates"; + public string FolderTemplateText => "Folder Template"; + public string FileTemplateText => "File Template"; + public string MultipartTemplateText => "Multipart File Template"; + public string ViewBookmarksText => "View _Bookmarks/Clips"; + public string ViewSeriesText => GridEntry.Liberate.IsSeries ? "View All Episodes in Series" : "View All Books in Series"; + + public bool LiberateEpisodesEnabled => GridEntry is ISeriesEntry sEntry && sEntry.Children.Any(c => c.Liberate.BookStatus is LiberatedStatus.NotLiberated or LiberatedStatus.PartialDownload); + public bool SetDownloadedEnabled => GridEntry.Book.UserDefinedItem.BookStatus != LiberatedStatus.Liberated || GridEntry.Liberate.IsSeries; + public bool SetNotDownloadedEnabled => GridEntry.Book.UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated || GridEntry.Liberate.IsSeries; + public bool ConvertToMp3Enabled => GridEntry.Book.UserDefinedItem.BookStatus is LiberatedStatus.Liberated; + public bool ReDownloadEnabled => GridEntry.Book.UserDefinedItem.BookStatus is LiberatedStatus.Liberated; + + public IGridEntry GridEntry { get; } + public char Accelerator { get; } + + public GridContextMenu(IGridEntry gridEntry, char accelerator) + { + GridEntry = gridEntry; + Accelerator = accelerator; + } + + public void SetDownloaded() + { + if (GridEntry is ISeriesEntry series) + { + series.Children.Select(c => c.LibraryBook).UpdateBookStatus(LiberatedStatus.Liberated); + } + else + { + GridEntry.LibraryBook.UpdateBookStatus(LiberatedStatus.Liberated); + } + } + + public void SetNotDownloaded() + { + if (GridEntry is ISeriesEntry series) + { + series.Children.Select(c => c.LibraryBook).UpdateBookStatus(LiberatedStatus.NotLiberated); + } + else + { + GridEntry.LibraryBook.UpdateBookStatus(LiberatedStatus.NotLiberated); + } + } + + public async Task RemoveAsync() + { + if (GridEntry is ISeriesEntry series) + { + await series.Children.Select(c => c.LibraryBook).RemoveBooksAsync(); + } + else + { + await Task.Run(GridEntry.LibraryBook.RemoveBook); + } + } + + public ITemplateEditor CreateTemplateEditor(LibraryBook libraryBook, string existingTemplate) + where T : Templates, ITemplate, new() + { + LibraryBookDto fileDto = libraryBook.ToDto(), folderDto = fileDto; + + if (libraryBook.Book.IsEpisodeChild() && + Configuration.Instance.SavePodcastsToParentFolder && + libraryBook.Book.SeriesLink.SingleOrDefault() is SeriesBook series) + { + using var context = DbContexts.GetContext(); + var seriesParent = context.GetLibraryBook_Flat_NoTracking(series.Series.AudibleSeriesId); + folderDto = seriesParent?.ToDto() ?? fileDto; + } + + return TemplateEditor.CreateFilenameEditor(Configuration.Instance.Books, existingTemplate, folderDto, fileDto); + } + + +} +class Command : ICommand +{ + public event EventHandler CanExecuteChanged; + + public bool CanExecute(object parameter) + { + throw new NotImplementedException(); + } + + public void Execute(object parameter) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/Source/LibationWinForms/GridView/ProductsDisplay.cs b/Source/LibationWinForms/GridView/ProductsDisplay.cs index 9b02d38d..64f391a4 100644 --- a/Source/LibationWinForms/GridView/ProductsDisplay.cs +++ b/Source/LibationWinForms/GridView/ProductsDisplay.cs @@ -102,19 +102,19 @@ namespace LibationWinForms.GridView private void productsGrid_CellContextMenuStripNeeded(IGridEntry entry, ContextMenuStrip ctxMenu) { + var ctx = new GridContextMenu(entry, '&'); #region Liberate all Episodes if (entry.Liberate.IsSeries) { var liberateEpisodesMenuItem = new ToolStripMenuItem() { - Text = "&Liberate All Episodes", - Enabled = ((ISeriesEntry)entry).Children.Any(c => c.Liberate.BookStatus is LiberatedStatus.NotLiberated or LiberatedStatus.PartialDownload) + Text = ctx.LiberateEpisodesText, + Enabled = ctx.LiberateEpisodesEnabled }; - ctxMenu.Items.Add(liberateEpisodesMenuItem); - liberateEpisodesMenuItem.Click += (_, _) => LiberateSeriesClicked?.Invoke(this, (ISeriesEntry)entry); + ctxMenu.Items.Add(liberateEpisodesMenuItem); } #endregion @@ -122,61 +122,44 @@ namespace LibationWinForms.GridView var setDownloadMenuItem = new ToolStripMenuItem() { - Text = "Set Download status to '&Downloaded'", - Enabled = entry.Book.UserDefinedItem.BookStatus != LiberatedStatus.Liberated || entry.Liberate.IsSeries + Text = ctx.SetDownloadedText, + Enabled = ctx.SetDownloadedEnabled }; - + setDownloadMenuItem.Click += (_, _) => ctx.SetDownloaded(); ctxMenu.Items.Add(setDownloadMenuItem); - if (entry.Liberate.IsSeries) - setDownloadMenuItem.Click += (_, _) => ((ISeriesEntry)entry).Children.Select(c => c.LibraryBook).UpdateBookStatus(LiberatedStatus.Liberated); - else - setDownloadMenuItem.Click += (_, _) => entry.LibraryBook.UpdateBookStatus(LiberatedStatus.Liberated); - #endregion #region Set Download status to Not Downloaded var setNotDownloadMenuItem = new ToolStripMenuItem() { - Text = "Set Download status to '&Not Downloaded'", - Enabled = entry.Book.UserDefinedItem.BookStatus != LiberatedStatus.NotLiberated || entry.Liberate.IsSeries + Text = ctx.SetNotDownloadedText, + Enabled = ctx.SetNotDownloadedEnabled }; - + setNotDownloadMenuItem.Click += (_, _) => ctx.SetNotDownloaded(); ctxMenu.Items.Add(setNotDownloadMenuItem); - if (entry.Liberate.IsSeries) - setNotDownloadMenuItem.Click += (_, _) => ((ISeriesEntry)entry).Children.Select(c => c.LibraryBook).UpdateBookStatus(LiberatedStatus.NotLiberated); - else - setNotDownloadMenuItem.Click += (_, _) => entry.LibraryBook.UpdateBookStatus(LiberatedStatus.NotLiberated); - #endregion #region Remove from library - var removeMenuItem = new ToolStripMenuItem() { Text = "&Remove from library" }; - + var removeMenuItem = new ToolStripMenuItem() { Text = ctx.RemoveText }; + removeMenuItem.Click += async (_, _) => await ctx.RemoveAsync(); ctxMenu.Items.Add(removeMenuItem); - if (entry.Liberate.IsSeries) - removeMenuItem.Click += async (_, _) => await ((ISeriesEntry)entry).Children.Select(c => c.LibraryBook).RemoveBooksAsync(); - else - removeMenuItem.Click += async (_, _) => await Task.Run(entry.LibraryBook.RemoveBook); - #endregion if (!entry.Liberate.IsSeries) { #region Locate file - var locateFileMenuItem = new ToolStripMenuItem() { Text = "&Locate file..." }; - + var locateFileMenuItem = new ToolStripMenuItem() { Text = ctx.LocateFileText }; ctxMenu.Items.Add(locateFileMenuItem); - locateFileMenuItem.Click += (_, _) => { try { var openFileDialog = new OpenFileDialog { - Title = $"Locate the audio file for '{entry.Book.TitleWithSubtitle}'", + Title = ctx.LocateFileDialogTitle, Filter = "All files (*.*)|*.*", FilterIndex = 1 }; @@ -185,8 +168,7 @@ namespace LibationWinForms.GridView } catch (Exception ex) { - var msg = "Error saving book's location"; - MessageBoxLib.ShowAdminAlert(this, msg, msg, ex); + MessageBoxLib.ShowAdminAlert(this, ctx.LocateFileErrorMessage, ctx.LocateFileErrorMessage, ex); } }; @@ -195,13 +177,11 @@ namespace LibationWinForms.GridView var convertToMp3MenuItem = new ToolStripMenuItem { - Text = "&Convert to Mp3", - Enabled = entry.Book.UserDefinedItem.BookStatus is LiberatedStatus.Liberated + Text = ctx.ConvertToMp3Text, + Enabled = ctx.ConvertToMp3Enabled }; - - ctxMenu.Items.Add(convertToMp3MenuItem); - convertToMp3MenuItem.Click += (_, e) => ConvertToMp3Clicked?.Invoke(this, entry.LibraryBook); + ctxMenu.Items.Add(convertToMp3MenuItem); #endregion } @@ -211,10 +191,9 @@ namespace LibationWinForms.GridView { var reDownloadMenuItem = new ToolStripMenuItem() { - Text = "Re-download this audiobook", - Enabled = entry.Book.UserDefinedItem.BookStatus is LiberatedStatus.Liberated + Text = ctx.ReDownloadText, + Enabled = ctx.ReDownloadEnabled }; - ctxMenu.Items.Add(reDownloadMenuItem); reDownloadMenuItem.Click += (s, _) => { @@ -223,6 +202,35 @@ namespace LibationWinForms.GridView LiberateClicked?.Invoke(s, entry.LibraryBook); }; } + #endregion + #region Edit Templates + void editTemplate(LibraryBook libraryBook, string existingTemplate, Action setNewTemplate) + where T : Templates, LibationFileManager.ITemplate, new() + { + var template = ctx.CreateTemplateEditor(libraryBook, existingTemplate); + var form = new EditTemplateDialog(template); + if (form.ShowDialog(this) == DialogResult.OK) + { + setNewTemplate(template.EditingTemplate.TemplateText); + } + } + + if (!entry.Liberate.IsSeries) + { + var folderTemplateMenuItem = new ToolStripMenuItem { Text = ctx.FolderTemplateText }; + var fileTemplateMenuItem = new ToolStripMenuItem { Text = ctx.FileTemplateText }; + var multiFileTemplateMenuItem = new ToolStripMenuItem { Text = ctx.MultipartTemplateText }; + folderTemplateMenuItem.Click += (s, _) => editTemplate(entry.LibraryBook, Configuration.Instance.FolderTemplate, t => Configuration.Instance.FolderTemplate = t); + fileTemplateMenuItem.Click += (s, _) => editTemplate(entry.LibraryBook, Configuration.Instance.FileTemplate, t => Configuration.Instance.FileTemplate = t); + multiFileTemplateMenuItem.Click += (s, _) => editTemplate(entry.LibraryBook, Configuration.Instance.ChapterFileTemplate, t => Configuration.Instance.ChapterFileTemplate = t); + + var editTemplatesMenuItem = new ToolStripMenuItem { Text = ctx.EditTemplatesText }; + editTemplatesMenuItem.DropDownItems.AddRange(new[] { folderTemplateMenuItem, fileTemplateMenuItem, multiFileTemplateMenuItem }); + + ctxMenu.Items.Add(new ToolStripSeparator()); + ctxMenu.Items.Add(editTemplatesMenuItem); + } + #endregion ctxMenu.Items.Add(new ToolStripSeparator()); @@ -231,11 +239,9 @@ namespace LibationWinForms.GridView if (!entry.Liberate.IsSeries) { - var bookRecordMenuItem = new ToolStripMenuItem { Text = "View &Bookmarks/Clips" }; - - ctxMenu.Items.Add(bookRecordMenuItem); - + var bookRecordMenuItem = new ToolStripMenuItem { Text = ctx.ViewBookmarksText }; bookRecordMenuItem.Click += (_, _) => new BookRecordsDialog(entry.LibraryBook).ShowDialog(this); + ctxMenu.Items.Add(bookRecordMenuItem); } #endregion @@ -243,13 +249,9 @@ namespace LibationWinForms.GridView if (entry.Book.SeriesLink.Any()) { - var header = entry.Liberate.IsSeries ? "View All Episodes in Series" : "View All Books in Series"; - - var viewSeriesMenuItem = new ToolStripMenuItem { Text = header }; - - ctxMenu.Items.Add(viewSeriesMenuItem); - + var viewSeriesMenuItem = new ToolStripMenuItem { Text = ctx.ViewSeriesText }; viewSeriesMenuItem.Click += (_, _) => new SeriesViewDialog(entry.LibraryBook).Show(); + ctxMenu.Items.Add(viewSeriesMenuItem); } #endregion