diff --git a/LangEditor/LangEdit.Designer.cs b/LangEditor/LangEdit.Designer.cs index d87be21..fcbabb7 100644 --- a/LangEditor/LangEdit.Designer.cs +++ b/LangEditor/LangEdit.Designer.cs @@ -30,9 +30,11 @@ private void InitializeComponent() { dgvMain = new System.Windows.Forms.DataGridView(); colID = new System.Windows.Forms.DataGridViewTextBoxColumn(); + colName = new System.Windows.Forms.DataGridViewTextBoxColumn(); colString = new System.Windows.Forms.DataGridViewTextBoxColumn(); statusStrip1 = new System.Windows.Forms.StatusStrip(); - menuStrip1 = new System.Windows.Forms.MenuStrip(); + lblStatus = new System.Windows.Forms.ToolStripStatusLabel(); +menuStrip1 = new System.Windows.Forms.MenuStrip(); fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); importToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); exportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -44,20 +46,24 @@ private void InitializeComponent() toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); hashToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); ((System.ComponentModel.ISupportInitialize)dgvMain).BeginInit(); + statusStrip1.SuspendLayout(); menuStrip1.SuspendLayout(); SuspendLayout(); // // dgvMain // dgvMain.AllowUserToOrderColumns = true; + dgvMain.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.Fill; dgvMain.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; - dgvMain.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { colID, colString }); + dgvMain.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { colID, colName, colString }); dgvMain.Dock = System.Windows.Forms.DockStyle.Fill; dgvMain.Location = new System.Drawing.Point(0, 24); dgvMain.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + dgvMain.MinimumSize = new System.Drawing.Size(70, 0); dgvMain.MultiSelect = false; dgvMain.Name = "dgvMain"; - dgvMain.Size = new System.Drawing.Size(484, 382); + dgvMain.RowHeadersWidth = 30; + dgvMain.Size = new System.Drawing.Size(648, 384); dgvMain.TabIndex = 0; dgvMain.RowsAdded += dgvMain_RowsAdded; // @@ -66,27 +72,38 @@ private void InitializeComponent() colID.HeaderText = "ID"; colID.Name = "colID"; // + // colName + // + colName.HeaderText = "Name"; + colName.Name = "colName"; + // // colString // colString.HeaderText = "String"; colString.Name = "colString"; // // statusStrip1 - // - statusStrip1.Location = new System.Drawing.Point(0, 406); + // + statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { lblStatus }); + statusStrip1.Location= new System.Drawing.Point(0, 408); statusStrip1.Name = "statusStrip1"; statusStrip1.Padding = new System.Windows.Forms.Padding(1, 0, 16, 0); - statusStrip1.Size = new System.Drawing.Size(484, 22); + statusStrip1.Size = new System.Drawing.Size(648, 22); statusStrip1.TabIndex = 1; statusStrip1.Text = "statusStrip1"; - // + // + // lblStatus + // + lblStatus.Name = "lblStatus"; + lblStatus.Size = new System.Drawing.Size(0, 17); +// // menuStrip1 // menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { fileToolStripMenuItem, editToolStripMenuItem, toolsToolStripMenuItem }); menuStrip1.Location = new System.Drawing.Point(0, 0); menuStrip1.Name = "menuStrip1"; menuStrip1.Padding = new System.Windows.Forms.Padding(7, 2, 0, 2); - menuStrip1.Size = new System.Drawing.Size(484, 24); + menuStrip1.Size = new System.Drawing.Size(648, 24); menuStrip1.TabIndex = 2; menuStrip1.Text = "menuStrip1"; // @@ -119,7 +136,8 @@ private void InitializeComponent() // applyChangesToolStripMenuItem // applyChangesToolStripMenuItem.Name = "applyChangesToolStripMenuItem"; - applyChangesToolStripMenuItem.Size = new System.Drawing.Size(163, 22); + applyChangesToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S; +applyChangesToolStripMenuItem.Size = new System.Drawing.Size(163, 22); applyChangesToolStripMenuItem.Text = "Apply Changes"; applyChangesToolStripMenuItem.Click += applyChangesToolStripMenuItem_Click; // @@ -142,7 +160,7 @@ private void InitializeComponent() // findNextToolStripMenuItem.Enabled = false; findNextToolStripMenuItem.Name = "findNextToolStripMenuItem"; - findNextToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift | System.Windows.Forms.Keys.F; + findNextToolStripMenuItem.ShortcutKeys = System.Windows.Forms.Keys.F3; findNextToolStripMenuItem.Size = new System.Drawing.Size(195, 22); findNextToolStripMenuItem.Text = "Find next"; findNextToolStripMenuItem.Click += findNextToolStripMenuItem_Click; @@ -165,7 +183,7 @@ private void InitializeComponent() // AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - ClientSize = new System.Drawing.Size(484, 428); + ClientSize = new System.Drawing.Size(648, 430); Controls.Add(dgvMain); Controls.Add(statusStrip1); Controls.Add(menuStrip1); @@ -174,6 +192,8 @@ private void InitializeComponent() Name = "LangEdit"; Text = "Language Editor"; ((System.ComponentModel.ISupportInitialize)dgvMain).EndInit(); + statusStrip1.ResumeLayout(false); + statusStrip1.PerformLayout(); menuStrip1.ResumeLayout(false); menuStrip1.PerformLayout(); ResumeLayout(false); @@ -184,7 +204,8 @@ private void InitializeComponent() private System.Windows.Forms.DataGridView dgvMain; private System.Windows.Forms.StatusStrip statusStrip1; - private System.Windows.Forms.MenuStrip menuStrip1; + private System.Windows.Forms.ToolStripStatusLabel lblStatus; +private System.Windows.Forms.MenuStrip menuStrip1; private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem importToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem exportToolStripMenuItem; @@ -195,6 +216,7 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem applyChangesToolStripMenuItem; private System.Windows.Forms.DataGridViewTextBoxColumn colID; + private System.Windows.Forms.DataGridViewTextBoxColumn colName; private System.Windows.Forms.DataGridViewTextBoxColumn colString; private System.Windows.Forms.ToolStripMenuItem findNextToolStripMenuItem; } diff --git a/LangEditor/LangEdit.cs b/LangEditor/LangEdit.cs index 41fea6a..42d0e88 100644 --- a/LangEditor/LangEdit.cs +++ b/LangEditor/LangEdit.cs @@ -4,6 +4,7 @@ using System.ComponentModel; using System.Globalization; using System.IO; +using System.Linq; using System.Text; using System.Windows.Forms; @@ -15,6 +16,9 @@ public partial class LangEdit : Form, IEntryEditor public event OnChanged Changed; private bool _ignoreChanges; + private bool _suppressHandlers; + private bool _dirty; // Grid has edits that haven't been applied to the resource + private object _originalCellValue; private string searchVal; private Dictionary dict; @@ -26,8 +30,12 @@ public Language Lang set { _ignoreChanges = true; + _suppressHandlers = true; + _lang = value; UpdateDisplay(); + + _suppressHandlers = false; _ignoreChanges = false; } } @@ -35,42 +43,176 @@ public Language Lang public LangEdit() { _ignoreChanges = true; + _suppressHandlers = true; + InitializeComponent(); + FormatCellDimensions(); GenerateDictionary(); + + dgvMain.CellBeginEdit += dgvMain_CellBeginEdit; + dgvMain.CellEndEdit += dgvMain_CellEndEdit; + dgvMain.CellValueChanged += dgvMain_CellValueChanged; + dgvMain.UserDeletedRow += (sender, e) => _dirty = true; + FormClosing += LangEdit_FormClosing; + + _suppressHandlers = false; _ignoreChanges = false; } + private void FormatCellDimensions() + { + dgvMain.RowTemplate.Height = 20; + + int width = TextRenderer.MeasureText("0x00000000", dgvMain.Font).Width + 16; + colID.AutoSizeMode = DataGridViewAutoSizeColumnMode.None; + colID.MinimumWidth = width; + colID.Width = width; + + // Strings can contain line breaks, so wrapping makes the cell editor multiline + colString.DefaultCellStyle.WrapMode = DataGridViewTriState.True; + } + + private void dgvMain_CellValueChanged(object sender, DataGridViewCellEventArgs e) + { + if (!_ignoreChanges && !_suppressHandlers) + _dirty = true; + } + + private void LangEdit_FormClosing(object sender, FormClosingEventArgs e) + { + dgvMain.EndEdit(); + if (!_dirty) + return; + + DialogResult result = MessageBox.Show(this, "Apply changes before closing?", "Unapplied changes", + MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); + if (result == DialogResult.Cancel || (result == DialogResult.Yes && RebuildLanguage() != 0)) + e.Cancel = true; + } + + private void dgvMain_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) + { + DataGridView grid = (DataGridView)sender; + _originalCellValue = grid[e.ColumnIndex, e.RowIndex].Value; + } + + private void dgvMain_CellEndEdit(object sender, DataGridViewCellEventArgs e) + { + if (_suppressHandlers) + return; + + DataGridView grid = (DataGridView)sender; + DataGridViewColumn column = grid.Columns[e.ColumnIndex]; + + if (column.Name != "colID" && column.Name != "colName") + return; + + DataGridViewCell cell = grid[e.ColumnIndex, e.RowIndex]; + object newValue = cell.Value; + + if (Equals(_originalCellValue, newValue)) + return; + + // A new or empty cell has nothing to overwrite, so only confirm when replacing a value + if (!string.IsNullOrEmpty(Convert.ToString(_originalCellValue))) + { + string associated = column.Name == "colName" ? "ID" : "Name"; + DialogResult result = MessageBox.Show( + this, + $"Are you sure you want to apply this change to this {column.HeaderText}?" + + $"\nThis will update the associated {associated} field!" + + $"\n\nNew: {newValue}\nOriginal: {_originalCellValue}", + "Confirm change", + MessageBoxButtons.YesNo, + MessageBoxIcon.Question + ); + + if (result == DialogResult.No) + { + _suppressHandlers = true; + cell.Value = _originalCellValue; + _suppressHandlers = false; + _originalCellValue = null; + return; + } + } + + _suppressHandlers = true; + try + { + DataGridViewRow row = grid.Rows[e.RowIndex]; + + if (column.Name == "colName") + { + string name = Convert.ToString(newValue) ?? string.Empty; + if (name.Length > 0) + { + uint id = Language.HashID(name); + row.Cells["colID"].Value = $"0x{id:X8}"; + } + } + else if (column.Name == "colID") + { + string idText = Convert.ToString(newValue) ?? string.Empty; + idText = idText.Trim(); + + if (idText.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) + idText = idText[2..]; + + if (!uint.TryParse(idText, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint id)) + { + MessageBox.Show( + this, + "ID must be a valid 32-bit hexadecimal value, for example 0x00000001.", + "Invalid ID", + MessageBoxButtons.OK, + MessageBoxIcon.Warning + ); + + cell.Value = _originalCellValue; + _originalCellValue = null; + return; + } + + row.Cells["colID"].Value = $"0x{id:X8}"; + + if (dict.TryGetValue(id, out string resolvedName)) + row.Cells["colName"].Value = resolvedName; + else + row.Cells["colName"].Value = string.Empty; + } + } + finally + { + _suppressHandlers = false; + _originalCellValue = null; + } + } + public void GenerateDictionary() { dict = new Dictionary(); - StreamReader keys = new StreamReader(AppContext.BaseDirectory + "keys/keys.txt", Encoding.UTF8); - string key; - while ((key = keys.ReadLine()) != null) + string keysDir = AppContext.BaseDirectory + "keys/"; + if (!File.Exists(keysDir + "keys.txt")) + return; + + // Names only label rows, so a duplicate key or hash collision keeps the first name + foreach (string key in File.ReadLines(keysDir + "keys.txt", Encoding.UTF8)) { if (key.Contains("") - subKeyFilePos); - StreamReader subKeys = new StreamReader(AppContext.BaseDirectory + "keys/" + subKeyFile + ".txt", Encoding.UTF8); - string subKey; - while ((subKey = subKeys.ReadLine()) != null) + if (!File.Exists(keysDir + subKeyFile + ".txt")) + continue; + foreach (string subKey in File.ReadLines(keysDir + subKeyFile + ".txt", Encoding.UTF8)) { string fullSubKey = key.Replace(subKeyFile, subKey); // Replace file name with subkey fullSubKey = fullSubKey.Remove(subKeyFilePos - 2, 2); // Remove - try - { - dict.Add(Language.HashID(fullSubKey), fullSubKey); - } - catch - { - MessageBox.Show("ID " + fullSubKey + " (0x" + Language.HashID(fullSubKey).ToString("X8") + ") already exists.", "Error", MessageBoxButtons.OK); - continue; - } + dict.TryAdd(Language.HashID(fullSubKey), fullSubKey); } - subKeys.Close(); - continue; } else if (key.Contains(" - dict.Add(Language.HashID(fullSubKey), fullSubKey); + dict.TryAdd(Language.HashID(fullSubKey), fullSubKey); } } - dict.Add(Language.HashID(key), key); + dict.TryAdd(Language.HashID(key), key); } - - keys.Close(); } public void UpdateDisplay() { - int resolved = 0, total = 0; + bool ignoreChanges = _ignoreChanges; + _ignoreChanges = true; + + int resolved = 0; foreach (KeyValuePair entry in _lang.mpEntries) { - if (dict.ContainsKey(entry.Key)) - { - dgvMain.Rows.Add(dict[entry.Key], entry.Value); - total++; + if (dict.TryGetValue(entry.Key, out string name)) resolved++; - } - else - { - dgvMain.Rows.Add("0x" + entry.Key.ToString("X8"), entry.Value); - total++; - } + dgvMain.Rows.Add($"0x{entry.Key:X8}", name ?? "", entry.Value); } - MessageBox.Show("Resolved " + resolved + "/" + total + " keys.", "Information", - MessageBoxButtons.OK); + + _ignoreChanges = ignoreChanges; + lblStatus.Text = $"{_lang.LanguageName}: {_lang.mpEntries.Count} strings, {resolved} names resolved"; + } + + private void SelectRow(DataGridViewRow row) + { + dgvMain.ClearSelection(); + dgvMain.CurrentCell = row.Cells[0]; + row.Selected = true; } public int RebuildLanguage() @@ -120,14 +263,19 @@ public int RebuildLanguage() if (_ignoreChanges) return 1; + dgvMain.EndEdit(); // Include the cell being edited Dictionary data = new Dictionary(); - for (int i = 0; i < dgvMain.Rows.Count - 1; ++i) + foreach (DataGridViewRow row in dgvMain.Rows) { - string idString = (string)dgvMain.Rows[i].Cells[0].Value; + if (row.IsNewRow) + continue; + + string idString = (string)row.Cells[0].Value; if (idString == null) { + SelectRow(row); MessageBox.Show(this, "ID cannot be left blank", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); return 2; @@ -138,7 +286,8 @@ public int RebuildLanguage() { if (!uint.TryParse(idString[2..], NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out id)) { - MessageBox.Show(this, "Failed to parse ID \"" + idString + "\"", "Warning", + SelectRow(row); + MessageBox.Show(this, "Failed to parse ID \"" + idString+ "\"", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); return 3; } @@ -148,9 +297,10 @@ public int RebuildLanguage() id = Language.HashID(idString); } - string value = (string)dgvMain.Rows[i].Cells[1].Value; + string value = (string)row.Cells[2].Value; if (data.ContainsKey(id)) { + SelectRow(row); MessageBox.Show(this, "ID " + idString + " already in use", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); return 4; @@ -159,6 +309,7 @@ public int RebuildLanguage() } _lang.mpEntries = data; + _dirty = false; Changed?.Invoke(); @@ -172,39 +323,119 @@ private void importToolStripMenuItem_Click(object sender, EventArgs e) if (openDialog.ShowDialog() == DialogResult.Cancel) return; - Dictionary data = new Dictionary(); - StreamReader file = new StreamReader(openDialog.FileName, Encoding.UTF8); - string line; - while ((line = file.ReadLine()) != null) + string error = ImportCSV(openDialog.FileName); + if (error != null) + MessageBox.Show(this, error + " Import cancelled.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + + internal string ImportCSV(string path) + { + List> rows = []; + try + { + using StreamReader reader = new(path, Encoding.UTF8); + foreach (List fields in ReadCSV(reader)) + rows.Add(fields); + } + catch (InvalidDataException) + { + return $"Row {rows.Count + 1} has a quote that is never closed."; + } + catch (IOException ex) + { + return ex.Message; + } + + // Exports from before the Name column have two columns, ID (or name) and string. + // This is decided per file, since a current-format row missing its string looks the same. + bool legacy = rows.All(fields => fields.Count <= 2); + + Dictionary data = []; + for (int row = 1; row <= rows.Count; row++) { - string[] pair = line.Split(',', 2); - pair[0] = pair[0].Remove(0, 1).Remove(pair[0].Length - 2, 1); // Remove quotes + List fields = rows[row - 1]; + string idText = fields[0].Trim(); + string name = !legacy && fields.Count > 1 ? fields[1].Trim() : ""; + uint key; - if (pair[0].StartsWith("0x") && pair[0].Length == 10) + if (idText.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) { - if (!uint.TryParse(pair[0][2..], NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out key)) - { - MessageBox.Show("Could not parse ID " + pair[0] + ". Import cancelled.", "Error", MessageBoxButtons.OK); - return; - } + if (!uint.TryParse(idText[2..], NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out key)) + return $"Could not parse ID {idText} on row {row}."; } + else if (idText.Length != 0) + key = Language.HashID(idText); + else if (name.Length != 0) + key = Language.HashID(name); else - key = Language.HashID(pair[0]); + return $"Row {row} has no ID or name."; - if (pair[1].Length != 0) // Non-empty value - { - pair[1] = pair[1].Substring(1, pair[1].Length - 2); // Remove enclosing double quotes - pair[1] = pair[1].Replace("\"\"", "\""); // Convert double quotes - pair[1] = pair[1].Replace("\\r", "\xD").Replace("\\n", "\xA"); // Convert newlines - } + string value = string.Join(",", fields.Skip(legacy ? 1 : 2)); // An unquoted string may contain commas + value = value.Replace("\\r", "\xD").Replace("\\n", "\xA"); // Convert newlines - data.Add(key, pair[1]); + if (!data.TryAdd(key, value)) + return $"ID {idText} on row {row} is a duplicate."; } _lang.mpEntries = data; dgvMain.Rows.Clear(); UpdateDisplay(); + _dirty = true; + return null; + } + + private static IEnumerable> ReadCSV(TextReader reader) + { + List fields = []; + StringBuilder field = new(); + bool quoted = false; + bool rowStarted = false; + int c; + while ((c = reader.Read()) != -1) + { + if (quoted) + { + if (c != '"') + field.Append((char)c); + else if (reader.Peek() == '"') + field.Append((char)reader.Read()); + else + quoted = false; + } + else if (c == '\r' || c == '\n') + { + if (c == '\r' && reader.Peek() == '\n') + reader.Read(); + if (rowStarted) + { + fields.Add(field.ToString()); + yield return fields; + } + fields = []; + field.Clear(); + rowStarted = false; + continue; + } + else if (c == '"') + quoted = true; + else if (c == ',') + { + fields.Add(field.ToString()); + field.Clear(); + } + else + field.Append((char)c); + rowStarted = true; + } + + if (quoted) + throw new InvalidDataException("Unterminated quote"); + if (rowStarted) + { + fields.Add(field.ToString()); + yield return fields; + } } private void exportToolStripMenuItem_Click(object sender, EventArgs e) @@ -214,31 +445,45 @@ private void exportToolStripMenuItem_Click(object sender, EventArgs e) if (saveDialog.ShowDialog() == DialogResult.Cancel) return; - // CSV content as a single contiguous string. - // Keys are prepended with 0x and padded to 8 characters. - // In values, incompatible characters are escaped. - // Both keys and values are enclosed in double quotes. - string languageContent = ""; - for (int i = 0; i < dgvMain.Rows.Count - 1; ++i) + string error = ExportCSV(saveDialog.FileName); + if (error == null) + MessageBox.Show("Export successful.", "Information", MessageBoxButtons.OK); + else + MessageBox.Show($"There was an issue exporting: {error}", "Error", MessageBoxButtons.OK); + } + + // Keys are prepended with 0x and padded to 8 characters. + // In values, incompatible characters are escaped. + // Keys and names are enclosed in double quotes, as are non-empty values. + internal string ExportCSV(string path) + { + dgvMain.EndEdit(); // Include the cell being edited + StringBuilder languageContent = new(); + foreach (DataGridViewRow row in dgvMain.Rows) { - languageContent += "\"" + (string)(dgvMain.Rows[i].Cells[0].Value) + "\","; - string tempValue = (string)dgvMain.Rows[i].Cells[1].Value; + if (row.IsNewRow) + continue; + + languageContent.Append('"').Append(row.Cells[0].Value).Append("\",\"").Append(row.Cells[1].Value).Append("\","); + + string tempValue = (string)row.Cells[2].Value ?? ""; tempValue = tempValue.Replace("\xD", "\\r").Replace("\xA", "\\n"); // Convert newlines tempValue = tempValue.Replace("\"", "\"\""); // Convert double quotes if (tempValue.Length != 0) // Enclose non-empty strings in double quotes - { - tempValue = "\"" + tempValue; - tempValue += "\""; - } - languageContent += tempValue + "\r\n"; + languageContent.Append('"').Append(tempValue).Append('"'); + languageContent.Append("\r\n"); } - FileStream file = (FileStream)saveDialog.OpenFile(); - file.Write(Encoding.UTF8.GetBytes(languageContent)); - file.Flush(); - file.Close(); - - MessageBox.Show("Export successful.", "Information", MessageBoxButtons.OK); + try + { + // The BOM lets spreadsheet apps detect UTF-8 + File.WriteAllText(path, languageContent.ToString(), new UTF8Encoding(true)); + } + catch (Exception ex) when (ex is IOException or UnauthorizedAccessException) + { + return ex.Message; + } + return null; } private void applyChangesToolStripMenuItem_Click(object sender, EventArgs e) @@ -246,7 +491,7 @@ private void applyChangesToolStripMenuItem_Click(object sender, EventArgs e) int result = RebuildLanguage(); if (result == 0) - MessageBox.Show(this, "Done!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information); + lblStatus.Text = $"{_lang.LanguageName}: {_lang.mpEntries.Count} strings, changes applied"; } private void findToolStripMenuItem_Click(object sender, EventArgs e) @@ -256,45 +501,36 @@ private void findToolStripMenuItem_Click(object sender, EventArgs e) return; searchVal = value; findNextToolStripMenuItem.Enabled = true; - - foreach (DataGridViewRow row in dgvMain.Rows) - { - if ((row.Cells[0].Value != null && ((string)row.Cells[0].Value).Contains(value, StringComparison.CurrentCultureIgnoreCase)) - || (row.Cells[1].Value != null && ((string)row.Cells[1].Value).Contains(value, StringComparison.CurrentCultureIgnoreCase))) - { - dgvMain.ClearSelection(); - dgvMain.CurrentCell = row.Cells[0]; - row.Selected = true; - return; - } - } - - MessageBox.Show(this, "Hash not found!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + FindFrom(0); } private void findNextToolStripMenuItem_Click(object sender, EventArgs e) { if (searchVal == null) return; - int start = dgvMain.SelectedCells[0].RowIndex + 1; - if (start > dgvMain.Rows.Count - 1) - { - MessageBox.Show(this, "Hash not found!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); - return; - } - for (int i = start; i < dgvMain.Rows.Count - 1; ++i) + FindFrom(dgvMain.CurrentCell == null ? 0 : dgvMain.CurrentCell.RowIndex + 1); + } + + private void FindFrom(int start) + { + int count = dgvMain.Rows.Count; + for (int i = 0; i < count; ++i) { - if (((string)dgvMain.Rows[i].Cells[0].Value).Contains(searchVal, StringComparison.CurrentCultureIgnoreCase) - || ((string)dgvMain.Rows[i].Cells[1].Value).Contains(searchVal, StringComparison.CurrentCultureIgnoreCase)) + DataGridViewRow row = dgvMain.Rows[(start + i) % count]; + if (row.IsNewRow) + continue; + + foreach (DataGridViewCell cell in row.Cells) { - dgvMain.ClearSelection(); - dgvMain.CurrentCell = dgvMain.Rows[i].Cells[0]; - dgvMain.Rows[i].Selected = true; - return; + if (cell.Value is string text && text.Contains(searchVal, StringComparison.CurrentCultureIgnoreCase)) + { + SelectRow(row); + return; + } } } - MessageBox.Show(this, "Hash not found!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + MessageBox.Show(this, $"\"{searchVal}\" not found.", "Find", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void hashToolStripMenuItem_Click(object sender, EventArgs e) @@ -304,7 +540,7 @@ private void hashToolStripMenuItem_Click(object sender, EventArgs e) return; uint result = Language.HashID(value); - MessageBox.Show(this, "Hashed value is: " + result.ToString("X8"), "Information", + MessageBox.Show(this, $"Hashed value is: 0x{result:X8}", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information); } diff --git a/LangEditor/LangEdit.resx b/LangEditor/LangEdit.resx index 9dec3c0..35811d6 100644 --- a/LangEditor/LangEdit.resx +++ b/LangEditor/LangEdit.resx @@ -1,4 +1,64 @@ - + + + @@ -60,6 +120,9 @@ True + + True + True diff --git a/LangEditor/Language.cs b/LangEditor/Language.cs index e77ce72..b0a959d 100644 --- a/LangEditor/Language.cs +++ b/LangEditor/Language.cs @@ -43,6 +43,8 @@ private enum LanguageId : uint private uint muSize; // Number of entries internal Dictionary mpEntries; + internal string LanguageName => meLanguageID.ToString().Replace("E_LANGUAGE_", ""); + public Language() { mpEntries = new Dictionary();