diff --git a/Source/FileManager/FileUtility.cs b/Source/FileManager/FileUtility.cs index 6cc42380..7a33dd91 100644 --- a/Source/FileManager/FileUtility.cs +++ b/Source/FileManager/FileUtility.cs @@ -84,12 +84,39 @@ namespace FileManager var pathNoPrefix = path.PathWithoutPrefix; - pathNoPrefix = replacements.ReplaceInvalidChars(pathNoPrefix); + pathNoPrefix = replaceInvalidChars(pathNoPrefix, replacements); pathNoPrefix = removeDoubleSlashes(pathNoPrefix); return pathNoPrefix; } + public static char[] invalidChars { get; } = Path.GetInvalidPathChars().Union(new[] { + '*', '?', ':', + // these are weird. If you run Path.GetInvalidPathChars() in Visual Studio's "C# Interactive", then these characters are included. + // In live code, Path.GetInvalidPathChars() does not include them + '"', '<', '>' + }).ToArray(); + private static string replaceInvalidChars(string path, ReplacementCharacters replacements) + { + // replace all colons except within the first 2 chars + var builder = new System.Text.StringBuilder(); + for (var i = 0; i < path.Length; i++) + { + var c = path[i]; + + if (!invalidChars.Contains(c) || (i <= 2 && Path.IsPathRooted(path))) + builder.Append(c); + else + { + char preceding = i > 0 ? path[i - 1] : default; + char succeeding = i < path.Length - 1 ? path[i + 1] : default; + builder.Append(replacements.GetReplacement(c, preceding, succeeding)); + } + + } + return builder.ToString(); + } + private static string removeDoubleSlashes(string path) { if (path.Length < 2) diff --git a/Source/FileManager/ReplacementCharacters.cs b/Source/FileManager/ReplacementCharacters.cs index a689e6f2..0fb91189 100644 --- a/Source/FileManager/ReplacementCharacters.cs +++ b/Source/FileManager/ReplacementCharacters.cs @@ -2,177 +2,43 @@ using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; -using System.IO; using System.Linq; namespace FileManager { public class Replacement { - public const int FIXED_COUNT = 4; + [JsonIgnore] + public bool Mandatory { get; set; } + [JsonProperty] + public char CharacterToReplace { get; init; } + [JsonProperty] + public string ReplacementString { get; set; } + [JsonProperty] + public string Description { get; set; } + + public Replacement Clone() => new() + { + Mandatory = Mandatory, + CharacterToReplace = CharacterToReplace, + ReplacementString = ReplacementString, + Description = Description + }; - internal const char QUOTE_MARK = '"'; - internal const string DEFAULT_DESCRIPTION = "Any other invalid characters"; - internal const string OPEN_QUOTE_DESCRIPTION = "Open Quote"; - internal const string CLOSE_QUOTE_DESCRIPTION = "Close Quote"; - internal const string OTHER_QUOTE_DESCRIPTION = "Other Quote"; - [JsonIgnore] public bool Mandatory { get; internal set; } - [JsonProperty] public char CharacterToReplace { get; private set; } - [JsonProperty] public string ReplacementString { get; private set; } - [JsonProperty] public string Description { get; private set; } public override string ToString() => $"{CharacterToReplace} → {ReplacementString} ({Description})"; - public Replacement(char charToReplace, string replacementString, string description) - { - CharacterToReplace = charToReplace; - ReplacementString = replacementString; - Description = description; - } - private Replacement(char charToReplace, string replacementString, string description, bool mandatory) - : this(charToReplace, replacementString, description) - { - Mandatory = mandatory; - } - - public void Update(char charToReplace, string replacementString, string description) - { - ReplacementString = replacementString; - - if (!Mandatory) - { - CharacterToReplace = charToReplace; - Description = description; - } - } - - public static Replacement OtherInvalid(string replacement) => new(default, replacement, DEFAULT_DESCRIPTION, true); - public static Replacement OpenQuote(string replacement) => new('"', replacement, OPEN_QUOTE_DESCRIPTION, true); - public static Replacement CloseQuote(string replacement) => new('"', replacement, CLOSE_QUOTE_DESCRIPTION, true); - public static Replacement OtherQuote(string replacement) => new('"', replacement, OTHER_QUOTE_DESCRIPTION, true); - public static Replacement Colon(string replacement) => new(':', replacement, "Colon"); - public static Replacement Asterisk(string replacement) => new('*', replacement, "Asterisk"); - public static Replacement QuestionMark(string replacement) => new('?', replacement, "Question Mark"); - public static Replacement OpenAngleBracket(string replacement) => new('<', replacement, "Open Angle Bracket"); - public static Replacement CloseAngleBracket(string replacement) => new('>', replacement, "Close Angle Bracket"); - public static Replacement Pipe(string replacement) => new('|', replacement, "Vertical Line"); + public static Replacement Colon(string replacement) => new Replacement { CharacterToReplace = ':', Description = "Colon", ReplacementString = replacement}; + public static Replacement Asterisk(string replacement) => new Replacement { CharacterToReplace = '*', Description = "Asterisk", ReplacementString = replacement }; + public static Replacement QuestionMark(string replacement) => new Replacement { CharacterToReplace = '?', Description = "Question Mark", ReplacementString = replacement }; + public static Replacement OpenAngleBracket(string replacement) => new Replacement { CharacterToReplace = '<', Description = "Open Angle Bracket", ReplacementString = replacement }; + public static Replacement CloseAngleBracket(string replacement) => new Replacement { CharacterToReplace = '>', Description = "Close Angle Bracket", ReplacementString = replacement }; + public static Replacement OpenQuote(string replacement) => new Replacement { CharacterToReplace = '"', Description = "Open Quote", ReplacementString = replacement }; + public static Replacement CloseQuote(string replacement) => new Replacement { CharacterToReplace = '"', Description = "Close Quote", ReplacementString = replacement }; + public static Replacement OtherQuote(string replacement) => new Replacement { CharacterToReplace = '"', Description = "Other Quote", ReplacementString = replacement }; + public static Replacement Pipe(string replacement) => new Replacement { CharacterToReplace = '|', Description = "Vertical Line", ReplacementString = replacement }; + public static Replacement OtherInvalid(string replacement) => new Replacement { CharacterToReplace = default, Description = "Any other invalid characters", ReplacementString = replacement }; } - [JsonConverter(typeof(ReplacementCharactersConverter))] - public class ReplacementCharacters - { - public static readonly ReplacementCharacters Default = new() - { - Replacements = new List() - { - Replacement.OtherInvalid("_"), - Replacement.OpenQuote("“"), - Replacement.CloseQuote("”"), - Replacement.OtherQuote("""), - Replacement.OpenAngleBracket("<"), - Replacement.CloseAngleBracket(">"), - Replacement.Colon("꞉"), - Replacement.Asterisk("✱"), - Replacement.QuestionMark("?"), - Replacement.Pipe("⏐"), - } - }; - - public static readonly ReplacementCharacters LoFiDefault = new() - { - Replacements = new List() - { - Replacement.OtherInvalid("_"), - Replacement.OpenQuote("'"), - Replacement.CloseQuote("'"), - Replacement.OtherQuote("'"), - Replacement.OpenAngleBracket("{"), - Replacement.CloseAngleBracket("}"), - Replacement.Colon("-"), - Replacement.Asterisk(""), - Replacement.QuestionMark(""), - } - }; - - public static readonly ReplacementCharacters Minimum = new() - { - Replacements = new List() - { - Replacement.OtherInvalid("_"), - Replacement.OpenQuote("_"), - Replacement.CloseQuote("_"), - Replacement.OtherQuote("_"), - } - }; - - private static readonly char[] invalidChars = Path.GetInvalidPathChars().Union(new[] { - '*', '?', ':', - // these are weird. If you run Path.GetInvalidPathChars() in Visual Studio's "C# Interactive", then these characters are included. - // In live code, Path.GetInvalidPathChars() does not include them - '"', '<', '>' - }).ToArray(); - - public IReadOnlyList Replacements { get; init; } - private string DefaultReplacement => Replacements[0].ReplacementString; - private string OpenQuote => Replacements[1].ReplacementString; - private string CloseQuote => Replacements[2].ReplacementString; - private string OtherQuote => Replacements[3].ReplacementString; - - private string GetReplacement(char toReplace, char preceding, char succeding) - { - if (toReplace == Replacement.QUOTE_MARK) - { - if ( - preceding != default - && !char.IsLetter(preceding) - && !char.IsNumber(preceding) - && (char.IsLetter(succeding) || char.IsNumber(succeding)) - ) - return OpenQuote; - else if ( - succeding != default - && !char.IsLetter(succeding) - && !char.IsNumber(succeding) - && (char.IsLetter(preceding) || char.IsNumber(preceding)) - ) - return CloseQuote; - else - return OtherQuote; - } - - for (int i = Replacement.FIXED_COUNT; i < Replacements.Count; i++) - { - var r = Replacements[i]; - if (r.CharacterToReplace == toReplace) - return r.ReplacementString; - } - return DefaultReplacement; - } - - public static bool ContainsInvalid(string path) - => path.Any(c => invalidChars.Contains(c)); - - public string ReplaceInvalidChars(string pathStr) - { - // replace all colons except within the first 2 chars - var builder = new System.Text.StringBuilder(); - for (var i = 0; i < pathStr.Length; i++) - { - var c = pathStr[i]; - - if (!invalidChars.Contains(c) || (i <= 2 && Path.IsPathRooted(pathStr))) - builder.Append(c); - else - { - char preceding = i > 0 ? pathStr[i - 1] : default; - char succeeding = i < pathStr.Length - 1 ? pathStr[i + 1] : default; - builder.Append(GetReplacement(c, preceding, succeeding)); - } - - } - return builder.ToString(); - } - } - #region JSON Converter internal class ReplacementCharactersConverter : JsonConverter { public override bool CanConvert(Type objectType) @@ -185,21 +51,22 @@ namespace FileManager var dict = replaceArr .ToObject().ToList(); - //Ensure that the first 4 replacements are for the expected chars and that all replacement strings are valid. - //If not, reset to default. - if (dict.Count < Replacement.FIXED_COUNT || - dict[0].CharacterToReplace != default || dict[0].Description != Replacement.DEFAULT_DESCRIPTION || - dict[1].CharacterToReplace != Replacement.QUOTE_MARK || dict[1].Description != Replacement.OPEN_QUOTE_DESCRIPTION || - dict[2].CharacterToReplace != Replacement.QUOTE_MARK || dict[2].Description != Replacement.CLOSE_QUOTE_DESCRIPTION || - dict[3].CharacterToReplace != Replacement.QUOTE_MARK || dict[3].Description != Replacement.OTHER_QUOTE_DESCRIPTION || - dict.Any(r => ReplacementCharacters.ContainsInvalid(r.ReplacementString)) - ) + //Add any missing defaults and ensure they are in the expected order. + for (int i = 0; i < ReplacementCharacters.Default.Replacements.Count; i++) { - dict = ReplacementCharacters.Default.Replacements; + var rep = ReplacementCharacters.Default.Replacements[i].Clone(); + + if (i < dict.Count) + { + var replacementStr = dict[i].ReplacementString; + dict[i] = rep; + dict[i].ReplacementString = replacementStr; + } + else + { + dict.Insert(i, rep); + } } - //First 4 are mandatory - for (int i = 0; i < Replacement.FIXED_COUNT; i++) - dict[i].Mandatory = true; return new ReplacementCharacters { Replacements = dict }; } @@ -218,5 +85,71 @@ namespace FileManager obj.WriteTo(writer); } } - #endregion + + [JsonConverter(typeof(ReplacementCharactersConverter))] + public class ReplacementCharacters + { + public static readonly ReplacementCharacters Default = new() + { + Replacements = new() + { + Replacement.OtherInvalid("_"), + Replacement.OpenQuote("“"), + Replacement.CloseQuote("”"), + Replacement.OtherQuote("""), + Replacement.Colon("꞉"), + Replacement.Asterisk("✱"), + Replacement.QuestionMark("?"), + Replacement.OpenAngleBracket("<"), + Replacement.CloseAngleBracket(">"), + Replacement.Pipe("⏐"), + } + }; + + public static readonly ReplacementCharacters LoFiDefault = new() + { + Replacements = new() + { + Replacement.OtherInvalid("_"), + Replacement.OpenQuote("'"), + Replacement.CloseQuote("'"), + Replacement.OtherQuote("'"), + Replacement.Colon("-"), + Replacement.Asterisk(""), + Replacement.QuestionMark(""), + Replacement.OpenAngleBracket("["), + Replacement.CloseAngleBracket("]"), + Replacement.Pipe("_"), + } + }; + + public List Replacements { get; init; } + public string DefaultReplacement => Replacements[0].ReplacementString; + public string OpenQuote => Replacements[1].ReplacementString; + public string CloseQuote => Replacements[2].ReplacementString; + public string OtherQuote => Replacements[3].ReplacementString; + + private const char QuoteMark = '"'; + + public string GetReplacement(char toReplace, char preceding, char succeding) + { + if (toReplace == QuoteMark) + { + if (preceding != default && !char.IsLetter(preceding) && !char.IsNumber(preceding)) + return OpenQuote; + else if (succeding != default && !char.IsLetter(succeding) && !char.IsNumber(succeding)) + return CloseQuote; + else + return OtherQuote; + } + + for (int i = 4; i < Replacements.Count; i++) + { + var r = Replacements[i]; + if (r.CharacterToReplace == toReplace) + return r.ReplacementString; + } + return DefaultReplacement; + } + } } diff --git a/Source/LibationWinForms/Dialogs/EditReplacementChars.Designer.cs b/Source/LibationWinForms/Dialogs/EditReplacementChars.Designer.cs index 07bced72..4bfe2c91 100644 --- a/Source/LibationWinForms/Dialogs/EditReplacementChars.Designer.cs +++ b/Source/LibationWinForms/Dialogs/EditReplacementChars.Designer.cs @@ -29,21 +29,18 @@ private void InitializeComponent() { this.dataGridView1 = new System.Windows.Forms.DataGridView(); + this.charToReplaceCol = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.replacementStringCol = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.descriptionCol = new System.Windows.Forms.DataGridViewTextBoxColumn(); this.defaultsBtn = new System.Windows.Forms.Button(); this.loFiDefaultsBtn = new System.Windows.Forms.Button(); this.saveBtn = new System.Windows.Forms.Button(); this.cancelBtn = new System.Windows.Forms.Button(); - this.minDefaultBtn = new System.Windows.Forms.Button(); - this.charToReplaceCol = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.replacementStringCol = new System.Windows.Forms.DataGridViewTextBoxColumn(); - this.descriptionCol = new System.Windows.Forms.DataGridViewTextBoxColumn(); ((System.ComponentModel.ISupportInitialize)(this.dataGridView1)).BeginInit(); this.SuspendLayout(); // // dataGridView1 // - this.dataGridView1.AllowUserToResizeColumns = false; - this.dataGridView1.AllowUserToResizeRows = false; this.dataGridView1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); @@ -55,12 +52,35 @@ this.dataGridView1.Location = new System.Drawing.Point(12, 12); this.dataGridView1.Name = "dataGridView1"; this.dataGridView1.RowTemplate.Height = 25; - this.dataGridView1.Size = new System.Drawing.Size(498, 393); + this.dataGridView1.Size = new System.Drawing.Size(416, 393); this.dataGridView1.TabIndex = 0; this.dataGridView1.CellEndEdit += new System.Windows.Forms.DataGridViewCellEventHandler(this.dataGridView1_CellEndEdit); + this.dataGridView1.CellValidating += new System.Windows.Forms.DataGridViewCellValidatingEventHandler(this.dataGridView1_CellValidating); this.dataGridView1.UserDeletingRow += new System.Windows.Forms.DataGridViewRowCancelEventHandler(this.dataGridView1_UserDeletingRow); this.dataGridView1.Resize += new System.EventHandler(this.dataGridView1_Resize); // + // charToReplaceCol + // + this.charToReplaceCol.HeaderText = "Char to Replace"; + this.charToReplaceCol.MinimumWidth = 70; + this.charToReplaceCol.Name = "charToReplaceCol"; + this.charToReplaceCol.Resizable = System.Windows.Forms.DataGridViewTriState.False; + this.charToReplaceCol.Width = 70; + // + // replacementStringCol + // + this.replacementStringCol.HeaderText = "Replacement String"; + this.replacementStringCol.MinimumWidth = 85; + this.replacementStringCol.Name = "replacementStringCol"; + this.replacementStringCol.Width = 85; + // + // descriptionCol + // + this.descriptionCol.HeaderText = "Description"; + this.descriptionCol.MinimumWidth = 100; + this.descriptionCol.Name = "descriptionCol"; + this.descriptionCol.Width = 200; + // // defaultsBtn // this.defaultsBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); @@ -86,7 +106,7 @@ // saveBtn // this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.saveBtn.Location = new System.Drawing.Point(428, 430); + this.saveBtn.Location = new System.Drawing.Point(346, 430); this.saveBtn.Name = "saveBtn"; this.saveBtn.Size = new System.Drawing.Size(82, 25); this.saveBtn.TabIndex = 1; @@ -97,7 +117,7 @@ // cancelBtn // this.cancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.cancelBtn.Location = new System.Drawing.Point(340, 430); + this.cancelBtn.Location = new System.Drawing.Point(258, 430); this.cancelBtn.Name = "cancelBtn"; this.cancelBtn.Size = new System.Drawing.Size(82, 25); this.cancelBtn.TabIndex = 1; @@ -105,45 +125,11 @@ this.cancelBtn.UseVisualStyleBackColor = true; this.cancelBtn.Click += new System.EventHandler(this.cancelBtn_Click); // - // minDefaultBtn - // - this.minDefaultBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.minDefaultBtn.Location = new System.Drawing.Point(172, 430); - this.minDefaultBtn.Name = "minDefaultBtn"; - this.minDefaultBtn.Size = new System.Drawing.Size(80, 25); - this.minDefaultBtn.TabIndex = 1; - this.minDefaultBtn.Text = "Barebones"; - this.minDefaultBtn.UseVisualStyleBackColor = true; - this.minDefaultBtn.Click += new System.EventHandler(this.minDefaultBtn_Click); - // - // charToReplaceCol - // - this.charToReplaceCol.HeaderText = "Char to Replace"; - this.charToReplaceCol.MinimumWidth = 70; - this.charToReplaceCol.Name = "charToReplaceCol"; - this.charToReplaceCol.Resizable = System.Windows.Forms.DataGridViewTriState.False; - this.charToReplaceCol.Width = 70; - // - // replacementStringCol - // - this.replacementStringCol.HeaderText = "Replacement Text"; - this.replacementStringCol.MinimumWidth = 85; - this.replacementStringCol.Name = "replacementStringCol"; - this.replacementStringCol.Width = 85; - // - // descriptionCol - // - this.descriptionCol.HeaderText = "Description"; - this.descriptionCol.MinimumWidth = 100; - this.descriptionCol.Name = "descriptionCol"; - this.descriptionCol.Width = 200; - // // EditReplacementChars // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(522, 467); - this.Controls.Add(this.minDefaultBtn); + this.ClientSize = new System.Drawing.Size(440, 467); this.Controls.Add(this.loFiDefaultsBtn); this.Controls.Add(this.cancelBtn); this.Controls.Add(this.saveBtn); @@ -159,13 +145,12 @@ #endregion private System.Windows.Forms.DataGridView dataGridView1; + private System.Windows.Forms.DataGridViewTextBoxColumn charToReplaceCol; + private System.Windows.Forms.DataGridViewTextBoxColumn replacementStringCol; + private System.Windows.Forms.DataGridViewTextBoxColumn descriptionCol; private System.Windows.Forms.Button defaultsBtn; private System.Windows.Forms.Button loFiDefaultsBtn; private System.Windows.Forms.Button saveBtn; private System.Windows.Forms.Button cancelBtn; - private System.Windows.Forms.Button minDefaultBtn; - private System.Windows.Forms.DataGridViewTextBoxColumn charToReplaceCol; - private System.Windows.Forms.DataGridViewTextBoxColumn replacementStringCol; - private System.Windows.Forms.DataGridViewTextBoxColumn descriptionCol; } } \ No newline at end of file diff --git a/Source/LibationWinForms/Dialogs/EditReplacementChars.cs b/Source/LibationWinForms/Dialogs/EditReplacementChars.cs index 11a0bf6c..0322ce64 100644 --- a/Source/LibationWinForms/Dialogs/EditReplacementChars.cs +++ b/Source/LibationWinForms/Dialogs/EditReplacementChars.cs @@ -15,30 +15,25 @@ namespace LibationWinForms.Dialogs InitializeComponent(); dataGridView1_Resize(this, EventArgs.Empty); } - public EditReplacementChars(Configuration config) : this() { this.config = config; LoadTable(config.ReplacementCharacters.Replacements); } - private void LoadTable(IReadOnlyList replacements) + private void LoadTable(List replacements) { dataGridView1.Rows.Clear(); - for (int i = 0; i < replacements.Count; i++) + foreach (var r in replacements) { - var r = replacements[i]; - int row = dataGridView1.Rows.Add(r.CharacterToReplace, r.ReplacementString, r.Description); dataGridView1.Rows[row].Tag = r; - - if (r.Mandatory) + if (ReplacementCharacters.Default.Replacements.Any(rep => rep.CharacterToReplace == r.CharacterToReplace)) { + r.Mandatory = true; dataGridView1.Rows[row].Cells[charToReplaceCol.Index].ReadOnly = true; dataGridView1.Rows[row].Cells[descriptionCol.Index].ReadOnly = true; - dataGridView1.Rows[row].Cells[charToReplaceCol.Index].Style.BackColor = System.Drawing.Color.LightGray; - dataGridView1.Rows[row].Cells[descriptionCol.Index].Style.BackColor = System.Drawing.Color.LightGray; } } } @@ -50,14 +45,67 @@ namespace LibationWinForms.Dialogs } private void loFiDefaultsBtn_Click(object sender, EventArgs e) - => LoadTable(ReplacementCharacters.LoFiDefault.Replacements); + { + LoadTable(ReplacementCharacters.LoFiDefault.Replacements); + } private void defaultsBtn_Click(object sender, EventArgs e) - => LoadTable(ReplacementCharacters.Default.Replacements); + { + LoadTable(ReplacementCharacters.Default.Replacements); + } - private void minDefaultBtn_Click(object sender, EventArgs e) - => LoadTable(ReplacementCharacters.Minimum.Replacements); + private void dataGridView1_CellValidating(object sender, DataGridViewCellValidatingEventArgs e) + { + if (e.RowIndex < 0) return; + var cellValue = e.FormattedValue?.ToString(); + + if (dataGridView1.Rows[e.RowIndex].Tag is Replacement row && row.Mandatory) + { + if (e.ColumnIndex == replacementStringCol.Index) + { + //Ensure replacement string doesn't contain an illegal character. + var replaceString = cellValue ?? string.Empty; + if (replaceString != string.Empty && replaceString.Any(c => FileUtility.invalidChars.Contains(c))) + { + dataGridView1.Rows[e.RowIndex].ErrorText = $"{replaceString} contains an illegal path character"; + e.Cancel = true; + } + } + return; + } + + + + if (e.ColumnIndex == charToReplaceCol.Index) + { + if (cellValue.Length != 1) + { + dataGridView1.Rows[e.RowIndex].ErrorText = "Only 1 character to replace per entry"; + e.Cancel = true; + } + else if ( + dataGridView1.Rows + .Cast() + .Where(r => r.Index != e.RowIndex) + .OfType() + .Any(r => r.CharacterToReplace == cellValue[0]) + ) + { + dataGridView1.Rows[e.RowIndex].ErrorText = $"The {cellValue[0]} character is already being replaced"; + e.Cancel = true; + } + } + else if (e.ColumnIndex == descriptionCol.Index || e.ColumnIndex == replacementStringCol.Index) + { + var value = dataGridView1.Rows[e.RowIndex].Cells[charToReplaceCol.Index].Value; + if (value is null || value is string str && string.IsNullOrEmpty(str)) + { + dataGridView1.Rows[e.RowIndex].ErrorText = $"You must choose a character to replace"; + e.Cancel = true; + } + } + } private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e) { @@ -65,14 +113,10 @@ namespace LibationWinForms.Dialogs dataGridView1.Rows[e.RowIndex].ErrorText = string.Empty; - var charToReplaceStr = dataGridView1.Rows[e.RowIndex].Cells[charToReplaceCol.Index].Value?.ToString(); - var replacement = dataGridView1.Rows[e.RowIndex].Cells[replacementStringCol.Index].Value?.ToString() ?? string.Empty; - var description = dataGridView1.Rows[e.RowIndex].Cells[descriptionCol.Index].Value?.ToString() ?? string.Empty; + var cellValue = dataGridView1.Rows[e.RowIndex].Cells[charToReplaceCol.Index].Value?.ToString(); - //Validate the whole row. If it passes all validation, add or update the row's tag. - if (string.IsNullOrEmpty(charToReplaceStr) && replacement == string.Empty && description == string.Empty) + if (string.IsNullOrEmpty(cellValue) || cellValue.Length > 1) { - //Invalid entry, so delete row var row = dataGridView1.Rows[e.RowIndex]; if (!row.IsNewRow) { @@ -82,38 +126,26 @@ namespace LibationWinForms.Dialogs })); } } - else if (string.IsNullOrEmpty(charToReplaceStr)) - { - dataGridView1.Rows[e.RowIndex].ErrorText = $"You must choose a character to replace"; - } - else if (charToReplaceStr.Length > 1) - { - dataGridView1.Rows[e.RowIndex].ErrorText = $"Only 1 {charToReplaceCol.HeaderText} per entry"; - } - else if (e.RowIndex >= Replacement.FIXED_COUNT && - dataGridView1.Rows - .Cast() - .Where(r => r.Index != e.RowIndex) - .Select(r => r.Tag) - .OfType() - .Any(r => r.CharacterToReplace == charToReplaceStr[0]) - ) - { - dataGridView1.Rows[e.RowIndex].ErrorText = $"The {charToReplaceStr[0]} character is already being replaced"; - } - else if (ReplacementCharacters.ContainsInvalid(replacement)) - { - dataGridView1.Rows[e.RowIndex].ErrorText = $"Your {replacementStringCol.HeaderText} contains illegal characters"; - } else { - //valid entry. Add or update Replacement in row's Tag - var charToReplace = charToReplaceStr[0]; + char charToReplace = cellValue[0]; + string description = dataGridView1.Rows[e.RowIndex].Cells[descriptionCol.Index].Value?.ToString() ?? string.Empty; + string replacement = dataGridView1.Rows[e.RowIndex].Cells[replacementStringCol.Index].Value?.ToString() ?? string.Empty; + var mandatory = false; if (dataGridView1.Rows[e.RowIndex].Tag is Replacement existing) - existing.Update(charToReplace, replacement, description); - else - dataGridView1.Rows[e.RowIndex].Tag = new Replacement(charToReplace, replacement, description); + { + mandatory = existing.Mandatory; + } + + dataGridView1.Rows[e.RowIndex].Tag = + new Replacement() + { + CharacterToReplace = charToReplace, + ReplacementString = replacement, + Description = description, + Mandatory = mandatory + }; } } @@ -123,6 +155,7 @@ namespace LibationWinForms.Dialogs .Cast() .Select(r => r.Tag) .OfType() + .Where(r => r.ReplacementString != null && (r.ReplacementString == string.Empty || !r.ReplacementString.Any(c => FileUtility.invalidChars.Contains(c)))) .ToList(); config.ReplacementCharacters = new ReplacementCharacters { Replacements = replacements }; diff --git a/Source/LibationWinForms/Dialogs/SettingsDialog.DownloadDecrypt.cs b/Source/LibationWinForms/Dialogs/SettingsDialog.DownloadDecrypt.cs index 035fdf98..8a5266e9 100644 --- a/Source/LibationWinForms/Dialogs/SettingsDialog.DownloadDecrypt.cs +++ b/Source/LibationWinForms/Dialogs/SettingsDialog.DownloadDecrypt.cs @@ -15,8 +15,7 @@ namespace LibationWinForms.Dialogs private void editCharreplacementBtn_Click(object sender, EventArgs e) { var form = new EditReplacementChars(config); - form.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; - form.ShowDialog(this); + form.ShowDialog(); } private void Load_DownloadDecrypt(Configuration config)