using System; using System.Collections.Generic; using System.Linq; using System.IO; using System.Windows.Forms; using Dinah.Core; using LibationFileManager; namespace LibationWinForms.Dialogs { public partial class EditTemplateDialog : Form { // final value. post-validity check public string TemplateText { get; private set; } // hold the work-in-progress value. not guaranteed to be valid private string _workingTemplateText; private string workingTemplateText { get => _workingTemplateText; set => _workingTemplateText = template.Sanitize(value, Configuration.Instance.ReplacementCharacters); } private void resetTextBox(string value) => this.templateTb.Text = workingTemplateText = value; private Configuration config { get; } = Configuration.Instance; private Templates template { get; } private string inputTemplateText { get; } public EditTemplateDialog() { InitializeComponent(); this.SetLibationIcon(); } public EditTemplateDialog(Templates template, string inputTemplateText) : this() { this.template = ArgumentValidator.EnsureNotNull(template, nameof(template)); this.inputTemplateText = inputTemplateText ?? ""; } private void EditTemplateDialog_Load(object sender, EventArgs e) { if (this.DesignMode) return; if (template is null) { MessageBoxLib.ShowAdminAlert(this, $"Programming error. {nameof(EditTemplateDialog)} was not created correctly", "Edit template error", new NullReferenceException($"{nameof(template)} is null")); return; } warningsLbl.Text = ""; this.Text = $"Edit {template.Name}"; this.templateLbl.Text = template.Description; resetTextBox(inputTemplateText); // populate list view foreach (var tag in template.GetTemplateTags()) listView1.Items.Add(new ListViewItem(new[] { $"<{tag.TagName}>", tag.Description }) { Tag = tag.DefaultValue }); } private void resetToDefaultBtn_Click(object sender, EventArgs e) => resetTextBox(template.DefaultTemplate); private void templateTb_TextChanged(object sender, EventArgs e) { workingTemplateText = templateTb.Text; var isChapterTitle = template == Templates.ChapterTitle; var isFolder = template == Templates.Folder; var libraryBookDto = new LibraryBookDto { Account = "my account", DateAdded = new DateTime(2022, 6, 9, 0, 0, 0), DatePublished = new DateTime(2017, 2, 27, 0, 0, 0), AudibleProductId = "123456789", Title = "A Study in Scarlet: A Sherlock Holmes Novel", Locale = "us", YearPublished = 2017, Authors = new List { "Arthur Conan Doyle", "Stephen Fry - introductions" }, Narrators = new List { "Stephen Fry" }, SeriesName = "Sherlock Holmes", SeriesNumber = "1", BitRate = 128, SampleRate = 44100, Channels = 2 }; var chapterName = "A Flight for Life"; var chapterNumber = 4; var chaptersTotal = 10; var partFileProperties = new AaxDecrypter.MultiConvertFileProperties() { OutputFileName = "", PartsPosition = chapterNumber, PartsTotal = chaptersTotal, Title = chapterName }; /* * Path must be rooted for windows to allow long file paths. This is * only necessary for folder templates because they may contain several * subdirectories. Without rooting, we won't be allowed to create a * relative path longer than MAX_PATH. */ var books = config.Books; var folder = Templates.Folder.GetPortionFilename( libraryBookDto, Path.Combine(books, isFolder ? workingTemplateText : config.FolderTemplate), ""); folder = Path.GetRelativePath(books, folder); var file = template == Templates.ChapterFile ? Templates.ChapterFile.GetPortionFilename( libraryBookDto, workingTemplateText, partFileProperties, "") : Templates.File.GetPortionFilename( libraryBookDto, isFolder ? config.FileTemplate : workingTemplateText, ""); var ext = config.DecryptToLossy ? "mp3" : "m4b"; var chapterTitle = Templates.ChapterTitle.GetPortionTitle(libraryBookDto, workingTemplateText, partFileProperties); const char ZERO_WIDTH_SPACE = '\u200B'; var sing = $"{Path.DirectorySeparatorChar}"; // result: can wrap long paths. eg: // |-- LINE WRAP BOUNDARIES --| // \books\author with a very <= normal line break on space between words // long name\narrator narrator // \title <= line break on the zero-with space we added before slashes string slashWrap(string val) => val.Replace(sing, $"{ZERO_WIDTH_SPACE}{sing}"); warningsLbl.Text = !template.HasWarnings(workingTemplateText) ? "" : "Warning:\r\n" + template .GetWarnings(workingTemplateText) .Select(err => $"- {err}") .Aggregate((a, b) => $"{a}\r\n{b}"); var bold = new System.Drawing.Font(richTextBox1.Font, System.Drawing.FontStyle.Bold); var reg = new System.Drawing.Font(richTextBox1.Font, System.Drawing.FontStyle.Regular); richTextBox1.Clear(); richTextBox1.SelectionFont = reg; if (isChapterTitle) { richTextBox1.SelectionFont = bold; richTextBox1.AppendText(chapterTitle); return; } richTextBox1.AppendText(slashWrap(books)); richTextBox1.AppendText(sing); if (isFolder) richTextBox1.SelectionFont = bold; richTextBox1.AppendText(slashWrap(folder)); if (isFolder) richTextBox1.SelectionFont = reg; richTextBox1.AppendText(sing); if (!isFolder) richTextBox1.SelectionFont = bold; richTextBox1.AppendText(file); if (!isFolder) richTextBox1.SelectionFont = reg; richTextBox1.AppendText($".{ext}"); } private void saveBtn_Click(object sender, EventArgs e) { if (!template.IsValid(workingTemplateText)) { var errors = template .GetErrors(workingTemplateText) .Select(err => $"- {err}") .Aggregate((a, b) => $"{a}\r\n{b}"); MessageBox.Show($"This template text is not valid. Errors:\r\n{errors}", "Invalid", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } TemplateText = workingTemplateText; this.DialogResult = DialogResult.OK; this.Close(); } private void cancelBtn_Click(object sender, EventArgs e) { this.DialogResult = DialogResult.Cancel; this.Close(); } private void listView1_DoubleClick(object sender, EventArgs e) { var itemText = listView1.SelectedItems[0].Tag as string; if (string.IsNullOrEmpty(itemText)) return; var text = templateTb.Text; var selStart = Math.Min(Math.Max(0, templateTb.SelectionStart), text.Length); templateTb.Text = text.Insert(selStart, itemText); templateTb.SelectionStart = selStart + itemText.Length; } } }