From 73868d06b6687d68d116cfc91310c85b77093135 Mon Sep 17 00:00:00 2001 From: "Derek W." Date: Tue, 26 Aug 2025 15:26:35 -0300 Subject: [PATCH 1/2] Add dark theme, CSV import/export, and UI enhancements Introduces a pure black dark theme with dark title bar, CSV import/export functionality for bulk editing, and custom icon support. Updates UI text, adds context menu options for CSV operations, and includes a new ExcelImporter utility. Solution and project files are updated to reflect these changes, and documentation is revised to describe the enhanced features. --- GT.RText-Release/GT.NDB0.Core.dll | Bin 0 -> 9728 bytes GT.RText-Release/GT.RText.Core.dll | Bin 0 -> 32768 bytes GT.RText-Release/GT.RText.exe | Bin 0 -> 45056 bytes GT.RText-Release/GT.RText.exe.config | 6 + GT.RText-Release/GT.Shared.dll | Bin 0 -> 19968 bytes GT.RText-Release/README.txt | 72 +++++ GT.RText-Release/app.ico | Bin 0 -> 4286 bytes GT.RText.sln | 12 +- GT.RText/DarkTheme.cs | 260 +++++++++++++++++ GT.RText/GT.RText.csproj | 13 +- GT.RText/Main.Designer.cs | 14 +- GT.RText/Main.cs | 420 +++++++++++++++++++++++++-- GT.RText/Properties/AssemblyInfo.cs | 8 +- GT.RText/RowEditor.Designer.cs | 2 +- GT.RText/RowEditor.cs | 36 +++ GT.RText/app.ico | Bin 0 -> 4286 bytes GT.RText/icon.txt | 1 + GT.RText/packages.config | 1 + GT.Shared/ExcelImporter.cs | 207 +++++++++++++ README.md | 85 +++++- 20 files changed, 1086 insertions(+), 51 deletions(-) create mode 100644 GT.RText-Release/GT.NDB0.Core.dll create mode 100644 GT.RText-Release/GT.RText.Core.dll create mode 100644 GT.RText-Release/GT.RText.exe create mode 100644 GT.RText-Release/GT.RText.exe.config create mode 100644 GT.RText-Release/GT.Shared.dll create mode 100644 GT.RText-Release/README.txt create mode 100644 GT.RText-Release/app.ico create mode 100644 GT.RText/DarkTheme.cs create mode 100644 GT.RText/app.ico create mode 100644 GT.RText/icon.txt create mode 100644 GT.Shared/ExcelImporter.cs diff --git a/GT.RText-Release/GT.NDB0.Core.dll b/GT.RText-Release/GT.NDB0.Core.dll new file mode 100644 index 0000000000000000000000000000000000000000..821533495234cf83c3e32341a6fddceb5916e867 GIT binary patch literal 9728 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 CW0W@l literal 0 HcmV?d00001 diff --git a/GT.RText-Release/GT.RText.exe b/GT.RText-Release/GT.RText.exe new file mode 100644 index 0000000000000000000000000000000000000000..c60959a0c1631f4482af7a6f638e580e736c1731 GIT binary patch literal 45056 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%0DAFQ40Zh0NX#`IJy;N7BR!G_|X_uteNxDnYq@-!2 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 F{{pk{!IS_1 literal 0 HcmV?d00001 diff --git a/GT.RText-Release/GT.RText.exe.config b/GT.RText-Release/GT.RText.exe.config new file mode 100644 index 0000000..56efbc7 --- /dev/null +++ b/GT.RText-Release/GT.RText.exe.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/GT.RText-Release/GT.Shared.dll b/GT.RText-Release/GT.Shared.dll new file mode 100644 index 0000000000000000000000000000000000000000..284b7f8c05c06c32a0bbca20ed34d5c7d0ed02c4 GIT binary patch literal 19968 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 I%m2I2AFmh_0ssI2 literal 0 HcmV?d00001 diff --git a/GT.RText.sln b/GT.RText.sln index bebc187..7372ea2 100644 --- a/GT.RText.sln +++ b/GT.RText.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.4.33103.184 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29709.97 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GT.RText", "GT.RText\GT.RText.csproj", "{9E8B67DF-F7CD-458C-848B-2B148466224E}" EndProject @@ -14,9 +14,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GT.NDB0.Core", "GT.NDB0\GT.NDB0.Core.csproj", "{EE1ACABD-0580-41F9-A56D-2676F2E6D608}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GT.RText.Comparer", "GT.RText.Comparer\GT.RText.Comparer.csproj", "{B4F8A315-F0EA-4B86-AE62-681AA5CD873E}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GT.NDB0.Core", "GT.NDB0\GT.NDB0.Core.csproj", "{EE1ACABD-0580-41F9-A56D-2676F2E6D608}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -40,10 +38,6 @@ Global {EE1ACABD-0580-41F9-A56D-2676F2E6D608}.Debug|Any CPU.Build.0 = Debug|Any CPU {EE1ACABD-0580-41F9-A56D-2676F2E6D608}.Release|Any CPU.ActiveCfg = Release|Any CPU {EE1ACABD-0580-41F9-A56D-2676F2E6D608}.Release|Any CPU.Build.0 = Release|Any CPU - {B4F8A315-F0EA-4B86-AE62-681AA5CD873E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B4F8A315-F0EA-4B86-AE62-681AA5CD873E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B4F8A315-F0EA-4B86-AE62-681AA5CD873E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B4F8A315-F0EA-4B86-AE62-681AA5CD873E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/GT.RText/DarkTheme.cs b/GT.RText/DarkTheme.cs new file mode 100644 index 0000000..064a29a --- /dev/null +++ b/GT.RText/DarkTheme.cs @@ -0,0 +1,260 @@ +using System; +using System.Drawing; +using System.Windows.Forms; + +namespace GT.RText +{ + /// + /// Class to apply dark theme to Windows Forms controls + /// + public static class DarkTheme + { + // Dark theme colors + public static readonly Color DarkBackground = Color.Black; + public static readonly Color DarkSecondaryBackground = Color.FromArgb(20, 20, 20); + public static readonly Color DarkControlBackground = Color.FromArgb(15, 15, 15); + public static readonly Color DarkForeground = Color.White; + public static readonly Color DarkBorder = Color.FromArgb(40, 40, 40); + public static readonly Color DarkSelection = Color.FromArgb(0, 122, 204); + public static readonly Color DarkMenuBackground = Color.FromArgb(20, 20, 20); + public static readonly Color DarkMenuForeground = Color.White; + public static readonly Color DarkStatusBackground = Color.FromArgb(0, 122, 204); + + /// + /// Applies dark theme to the main form and all its controls + /// + /// Form to be modified + public static void ApplyDarkTheme(Form form) + { + // Configure main form + form.BackColor = DarkBackground; + form.ForeColor = DarkForeground; + + // Apply theme to all controls recursively + ApplyDarkThemeToControls(form.Controls); + } + + /// + /// Applies dark theme to a collection of controls recursively + /// + /// Control collection + private static void ApplyDarkThemeToControls(Control.ControlCollection controls) + { + foreach (Control control in controls) + { + ApplyDarkThemeToControl(control); + + // Apply recursively to child controls + if (control.HasChildren) + { + ApplyDarkThemeToControls(control.Controls); + } + } + } + + /// + /// Applies dark theme to a specific control + /// + /// Control to be modified + private static void ApplyDarkThemeToControl(Control control) + { + try + { + switch (control) + { + case MenuStrip menuStrip: + ApplyDarkThemeToMenuStrip(menuStrip); + break; + + case ContextMenuStrip contextMenu: + ApplyDarkThemeToContextMenu(contextMenu); + break; + + case StatusStrip statusStrip: + ApplyDarkThemeToStatusStrip(statusStrip); + break; + + case TabControl tabControl: + ApplyDarkThemeToTabControl(tabControl); + break; + + case ListView listView: + ApplyDarkThemeToListView(listView); + break; + + case TextBox textBox: + ApplyDarkThemeToTextBox(textBox); + break; + + case Button button: + ApplyDarkThemeToButton(button); + break; + + case Panel panel: + ApplyDarkThemeToPanel(panel); + break; + + case GroupBox groupBox: + ApplyDarkThemeToGroupBox(groupBox); + break; + + case Label label: + ApplyDarkThemeToLabel(label); + break; + + default: + // Apply basic theme for non-specific controls + control.BackColor = DarkBackground; + control.ForeColor = DarkForeground; + break; + } + } + catch (Exception) + { + // Ignore errors from controls that don't support color changes + } + } + + private static void ApplyDarkThemeToMenuStrip(MenuStrip menuStrip) + { + menuStrip.BackColor = DarkMenuBackground; + menuStrip.ForeColor = DarkMenuForeground; + menuStrip.Renderer = new DarkMenuRenderer(); + + foreach (ToolStripItem item in menuStrip.Items) + { + ApplyDarkThemeToMenuItem(item); + } + } + + private static void ApplyDarkThemeToContextMenu(ContextMenuStrip contextMenu) + { + contextMenu.BackColor = DarkMenuBackground; + contextMenu.ForeColor = DarkMenuForeground; + contextMenu.Renderer = new DarkMenuRenderer(); + + foreach (ToolStripItem item in contextMenu.Items) + { + ApplyDarkThemeToMenuItem(item); + } + } + + private static void ApplyDarkThemeToMenuItem(ToolStripItem item) + { + item.BackColor = DarkMenuBackground; + item.ForeColor = DarkMenuForeground; + + if (item is ToolStripMenuItem menuItem && menuItem.HasDropDownItems) + { + foreach (ToolStripItem subItem in menuItem.DropDownItems) + { + ApplyDarkThemeToMenuItem(subItem); + } + } + } + + private static void ApplyDarkThemeToStatusStrip(StatusStrip statusStrip) + { + statusStrip.BackColor = DarkSecondaryBackground; + statusStrip.ForeColor = DarkForeground; + statusStrip.Renderer = new DarkStatusStripRenderer(); + + foreach (ToolStripItem item in statusStrip.Items) + { + item.BackColor = DarkSecondaryBackground; + item.ForeColor = DarkForeground; + } + } + + private static void ApplyDarkThemeToTabControl(TabControl tabControl) + { + tabControl.BackColor = DarkBackground; + tabControl.ForeColor = DarkForeground; + + foreach (TabPage tabPage in tabControl.TabPages) + { + tabPage.BackColor = DarkBackground; + tabPage.ForeColor = DarkForeground; + } + } + + private static void ApplyDarkThemeToListView(ListView listView) + { + listView.BackColor = DarkControlBackground; + listView.ForeColor = DarkForeground; + listView.BorderStyle = BorderStyle.FixedSingle; + } + + private static void ApplyDarkThemeToTextBox(TextBox textBox) + { + textBox.BackColor = DarkControlBackground; + textBox.ForeColor = DarkForeground; + textBox.BorderStyle = BorderStyle.FixedSingle; + } + + private static void ApplyDarkThemeToButton(Button button) + { + button.BackColor = DarkSecondaryBackground; + button.ForeColor = DarkForeground; + button.FlatStyle = FlatStyle.Flat; + button.FlatAppearance.BorderColor = DarkBorder; + button.FlatAppearance.MouseOverBackColor = DarkSelection; + } + + private static void ApplyDarkThemeToPanel(Panel panel) + { + panel.BackColor = DarkBackground; + panel.ForeColor = DarkForeground; + } + + private static void ApplyDarkThemeToGroupBox(GroupBox groupBox) + { + groupBox.BackColor = DarkBackground; + groupBox.ForeColor = DarkForeground; + } + + private static void ApplyDarkThemeToLabel(Label label) + { + label.BackColor = Color.Transparent; + label.ForeColor = DarkForeground; + } + } + + /// + /// Renderer personalizado para menus em tema escuro + /// + public class DarkMenuRenderer : ToolStripProfessionalRenderer + { + public DarkMenuRenderer() : base(new DarkMenuColorTable()) { } + } + + /// + /// Renderer personalizado para status strip em tema escuro + /// + public class DarkStatusStripRenderer : ToolStripProfessionalRenderer + { + public DarkStatusStripRenderer() : base(new DarkMenuColorTable()) { } + } + + /// + /// Tabela de cores personalizada para menus em tema escuro + /// + public class DarkMenuColorTable : ProfessionalColorTable + { + public override Color MenuItemSelected => DarkTheme.DarkSelection; + public override Color MenuItemSelectedGradientBegin => DarkTheme.DarkSelection; + public override Color MenuItemSelectedGradientEnd => DarkTheme.DarkSelection; + public override Color MenuItemPressedGradientBegin => DarkTheme.DarkSelection; + public override Color MenuItemPressedGradientEnd => DarkTheme.DarkSelection; + public override Color MenuItemBorder => DarkTheme.DarkBorder; + public override Color MenuBorder => DarkTheme.DarkBorder; + public override Color MenuStripGradientBegin => DarkTheme.DarkMenuBackground; + public override Color MenuStripGradientEnd => DarkTheme.DarkMenuBackground; + public override Color ToolStripDropDownBackground => DarkTheme.DarkMenuBackground; + public override Color ImageMarginGradientBegin => DarkTheme.DarkMenuBackground; + public override Color ImageMarginGradientMiddle => DarkTheme.DarkMenuBackground; + public override Color ImageMarginGradientEnd => DarkTheme.DarkMenuBackground; + public override Color SeparatorDark => DarkTheme.DarkBorder; + public override Color SeparatorLight => DarkTheme.DarkBorder; + } +} diff --git a/GT.RText/GT.RText.csproj b/GT.RText/GT.RText.csproj index a9f3c28..62fd18a 100644 --- a/GT.RText/GT.RText.csproj +++ b/GT.RText/GT.RText.csproj @@ -35,16 +35,7 @@ 4 - - ..\packages\Costura.Fody.3.3.3\lib\net40\Costura.dll - - - ..\packages\Microsoft-WindowsAPICodePack-Core.1.1.4\lib\net472\Microsoft.WindowsAPICodePack.dll - - - ..\packages\Microsoft-WindowsAPICodePack-Shell.1.1.4\lib\net472\Microsoft.WindowsAPICodePack.Shell.dll - @@ -63,6 +54,7 @@ + @@ -107,6 +99,9 @@ + + Always + diff --git a/GT.RText/Main.Designer.cs b/GT.RText/Main.Designer.cs index d5aff52..335d222 100644 --- a/GT.RText/Main.Designer.cs +++ b/GT.RText/Main.Designer.cs @@ -34,6 +34,7 @@ private void InitializeComponent() { this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.editToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.addEditFromCSVFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.importExcelToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); this.editToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.addToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -116,7 +117,8 @@ private void InitializeComponent() { // editToolStripMenuItem1 // this.editToolStripMenuItem1.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.addEditFromCSVFileToolStripMenuItem}); + this.addEditFromCSVFileToolStripMenuItem, + this.importExcelToolStripMenuItem}); this.editToolStripMenuItem1.Name = "editToolStripMenuItem1"; this.editToolStripMenuItem1.Size = new System.Drawing.Size(39, 20); this.editToolStripMenuItem1.Text = "Edit"; @@ -128,6 +130,13 @@ private void InitializeComponent() { this.addEditFromCSVFileToolStripMenuItem.Text = "Add/Edit Current Category from CSV File (Key, Value)"; this.addEditFromCSVFileToolStripMenuItem.Click += new System.EventHandler(this.addEditFromCSVFileToolStripMenuItem_Click); // + // importExcelToolStripMenuItem + // + this.importExcelToolStripMenuItem.Name = "importExcelToolStripMenuItem"; + this.importExcelToolStripMenuItem.Size = new System.Drawing.Size(353, 22); + this.importExcelToolStripMenuItem.Text = "Import CSV for Current Category (RecNo, Label, String)"; + this.importExcelToolStripMenuItem.Click += new System.EventHandler(this.importExcelToolStripMenuItem_Click); + // // contextMenuStrip // this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { @@ -239,7 +248,7 @@ private void InitializeComponent() { this.Controls.Add(this.menuStrip); this.MainMenuStrip = this.menuStrip; this.Name = "Main"; - this.Text = "RT2 Editor by xfileFIN"; + this.Text = "GT.R Text Editor (Modified by Derek W 🏴‍☠️)"; this.SizeChanged += new System.EventHandler(this.Main_SizeChanged); this.menuStrip.ResumeLayout(false); this.menuStrip.PerformLayout(); @@ -274,6 +283,7 @@ private void InitializeComponent() { private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; private System.Windows.Forms.ToolStripMenuItem editToolStripMenuItem1; private System.Windows.Forms.ToolStripMenuItem addEditFromCSVFileToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem importExcelToolStripMenuItem; private System.Windows.Forms.OpenFileDialog csvOpenFileDialog; } } diff --git a/GT.RText/Main.cs b/GT.RText/Main.cs index d66b795..9cb3bf1 100644 --- a/GT.RText/Main.cs +++ b/GT.RText/Main.cs @@ -1,21 +1,43 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.IO; using System.Windows.Forms; +using System.Runtime.InteropServices; // Required for the non crappy folder picker // https://stackoverflow.com/q/11624298 -using Microsoft.WindowsAPICodePack.Dialogs; +// using Microsoft.WindowsAPICodePack.Dialogs; using GT.RText.Core; using GT.RText.Core.Exceptions; using GT.Shared.Logging; +using GT.Shared; using System.Linq; namespace GT.RText { public partial class Main : Form { + // Windows API declarations for dark title bar + [DllImport("dwmapi.dll", PreserveSig = true)] + private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize); + + [DllImport("dwmapi.dll")] + private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMarInset); + + [StructLayout(LayoutKind.Sequential)] + private struct MARGINS + { + public int leftWidth; + public int rightWidth; + public int topHeight; + public int bottomHeight; + } + + private const int DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1 = 19; + private const int DWMWA_USE_IMMERSIVE_DARK_MODE = 20; + /// /// Designates whether the currently loaded content is a project folder. /// @@ -33,6 +55,7 @@ public partial class Main : Form private List _rTexts; private ListViewColumnSorter _columnSorter; + private ContextMenuStrip _categoriesContextMenu; public RTextParser CurrentRText => _rTexts[tabControlLocalFiles.SelectedIndex]; public RTextPageBase CurrentPage { get; set; } @@ -47,6 +70,54 @@ public Main() _columnSorter = new ListViewColumnSorter(); this.listViewEntries.ListViewItemSorter = _columnSorter; this.listViewEntries.Sorting = SortOrder.Ascending; + + InitializeExcelImportFeature(); + + // Apply dark theme + DarkTheme.ApplyDarkTheme(this); + + // Apply dark title bar + this.Load += (s, e) => ApplyDarkTitleBar(); + + // Load icon if exists + try + { + string iconPath = Path.Combine(Application.StartupPath, "app.ico"); + if (File.Exists(iconPath)) + { + this.Icon = new Icon(iconPath); + } + } + catch + { + // Ignore if unable to load the icon + } + } + + /// + /// Applies dark title bar using Windows API + /// + private void ApplyDarkTitleBar() + { + try + { + if (this.Handle != IntPtr.Zero) + { + int value = 1; + // Try first the newest API version + int result = DwmSetWindowAttribute(this.Handle, DWMWA_USE_IMMERSIVE_DARK_MODE, ref value, sizeof(int)); + + // If it fails, try the older version + if (result != 0) + { + DwmSetWindowAttribute(this.Handle, DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, ref value, sizeof(int)); + } + } + } + catch + { + // Ignore if API is not available + } } #region Events @@ -71,13 +142,11 @@ private void openToolStripMenuItem_Click(object sender, EventArgs e) private void openFolderToolStripMenuItem_Click(object sender, EventArgs e) { - var dialog = new CommonOpenFileDialog(); - dialog.EnsureFileExists = true; - dialog.EnsurePathExists = true; - - dialog.IsFolderPicker = true; + var dialog = new FolderBrowserDialog(); + dialog.Description = "Selecione a pasta com os arquivos RT"; + dialog.ShowNewFolderButton = false; - if (dialog.ShowDialog() != CommonFileDialogResult.Ok) return; + if (dialog.ShowDialog() != DialogResult.OK) return; _rTexts.Clear(); @@ -87,7 +156,7 @@ private void openFolderToolStripMenuItem_Click(object sender, EventArgs e) ClearTabs(); bool firstTab = true; - string[] files = Directory.GetFiles(dialog.FileName, "*", SearchOption.TopDirectoryOnly); + string[] files = Directory.GetFiles(dialog.SelectedPath, "*", SearchOption.TopDirectoryOnly); if (files.Any(f => RTextParser.Locales.ContainsKey(Path.GetFileNameWithoutExtension(f)))) { @@ -118,7 +187,7 @@ private void openFolderToolStripMenuItem_Click(object sender, EventArgs e) else { // Locale files are located per-UI project, in their own folder (i.e arcade/US/rtext.rt2) - string[] folders = Directory.GetDirectories(dialog.FileName, "*", SearchOption.TopDirectoryOnly); + string[] folders = Directory.GetDirectories(dialog.SelectedPath, "*", SearchOption.TopDirectoryOnly); foreach (var folder in folders) { string actualDirName = Path.GetFileName(folder); @@ -152,24 +221,22 @@ private void saveToolStripMenuItem_Click(object sender, EventArgs e) { if (_isUiFolderProject) { - var dialog = new CommonOpenFileDialog(); - dialog.EnsureFileExists = true; - dialog.EnsurePathExists = true; + var dialog = new FolderBrowserDialog(); + dialog.Description = "Selecione a pasta para salvar os arquivos"; + dialog.ShowNewFolderButton = true; - dialog.IsFolderPicker = true; - - if (dialog.ShowDialog() != CommonFileDialogResult.Ok) return; + if (dialog.ShowDialog() != DialogResult.OK) return; foreach (var rtext in _rTexts) { if (_isGT6AndAboveProjectStyle) { - string localePath = Path.Combine(dialog.FileName, $"{rtext.LocaleCode}.rt2"); + string localePath = Path.Combine(dialog.SelectedPath, $"{rtext.LocaleCode}.rt2"); rtext.RText.Save(localePath); } else { - string localePath = Path.Combine(dialog.FileName, rtext.LocaleCode); + string localePath = Path.Combine(dialog.SelectedPath, rtext.LocaleCode); Directory.CreateDirectory(localePath); rtext.RText.Save(Path.Combine(localePath, "rtext.rt2")); @@ -485,6 +552,19 @@ private void addEditFromCSVFileToolStripMenuItem_Click(object sender, EventArgs DisplayEntries(CurrentPage); } + + private void importExcelToolStripMenuItem_Click(object sender, EventArgs e) + { + if (!_rTexts.Any() || CurrentRText is null || CurrentPage is null) + { + MessageBox.Show("No file loaded or category selected.", "Warning", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + ImportExcelForCategory_Click(sender, e); + } + #endregion private void ClearTabs() @@ -618,5 +698,311 @@ private void SortEntriesListView(int columnIndex) // Perform the sort with these new sort options. this.listViewEntries.Sort(); } + + #region Excel Import Feature + + private void InitializeExcelImportFeature() + { + // Criar menu de contexto para as categorias + _categoriesContextMenu = new ContextMenuStrip(); + + var importCsvItem = new ToolStripMenuItem("Import CSV to this category"); + importCsvItem.Click += ImportExcelForCategory_Click; + importCsvItem.Image = null; // Pode adicionar um ícone se desejar + + var exportCsvItem = new ToolStripMenuItem("Export this category to CSV"); + exportCsvItem.Click += ExportCategoryToCsv_Click; + exportCsvItem.Image = null; // Pode adicionar um ícone se desejar + + var createSampleItem = new ToolStripMenuItem("Create a CSV in the correct format"); + createSampleItem.Click += CreateSampleCsv_Click; + + _categoriesContextMenu.Items.Add(importCsvItem); + _categoriesContextMenu.Items.Add(exportCsvItem); + _categoriesContextMenu.Items.Add(new ToolStripSeparator()); + _categoriesContextMenu.Items.Add(createSampleItem); + + // Associar o menu de contexto ao ListView de páginas/categorias + listViewPages.ContextMenuStrip = _categoriesContextMenu; + } + + private void CreateSampleCsv_Click(object sender, EventArgs e) + { + var saveFileDialog = new SaveFileDialog + { + Filter = "CSV Files (*.csv)|*.csv", + Title = "Save sample CSV file", + FileName = "sample_import.csv" + }; + + if (saveFileDialog.ShowDialog(this) == DialogResult.OK) + { + try + { + ExcelImporter.CreateSampleCsv(saveFileDialog.FileName); + MessageBox.Show($"Sample file created successfully!\n\nLocation: {saveFileDialog.FileName}\n\n" + + "You can edit this file and use it to import data.", + "File created", MessageBoxButtons.OK, MessageBoxIcon.Information); + + toolStripStatusLabel.Text = "Sample CSV file created successfully"; + } + catch (Exception ex) + { + MessageBox.Show($"Error creating sample file: {ex.Message}", "Error", + MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + } + + private void ImportExcelForCategory_Click(object sender, EventArgs e) + { + // Verificar se há uma categoria selecionada + if (listViewPages.SelectedItems.Count <= 0 || listViewPages.SelectedItems[0] == null) + { + MessageBox.Show("Please select a category first.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + var selectedItem = listViewPages.SelectedItems[0]; + var page = (RTextPageBase)selectedItem.Tag; + var categoryName = page.Name; + + var openFileDialog = new OpenFileDialog + { + Filter = "CSV Files (*.csv)|*.csv|All files (*.*)|*.*", + Title = "Select CSV file for import", + CheckFileExists = true, + CheckPathExists = true + }; + + if (openFileDialog.ShowDialog(this) == DialogResult.OK) + { + ImportCsvData(openFileDialog.FileName, page, categoryName); + } + } + + private void ImportCsvData(string filePath, RTextPageBase page, string categoryName) + { + try + { + // Mostrar cursor de espera + this.Cursor = Cursors.WaitCursor; + toolStripStatusLabel.Text = "Importing CSV data..."; + + var importResult = ExcelImporter.ImportFromCsv(filePath); + + if (!importResult.Success) + { + MessageBox.Show($"Import error: {importResult.Message}", "Error", + MessageBoxButtons.OK, MessageBoxIcon.Error); + return; + } + + // Mostrar prévia e confirmar importação + var previewMessage = $"File: {Path.GetFileName(filePath)}\n" + + $"Category: {categoryName}\n" + + $"Records found: {importResult.ImportedEntries.Count}\n\n" + + "First records:\n"; + + // Mostrar os primeiros 5 registros como prévia + var preview = importResult.ImportedEntries.Take(5); + foreach (var entry in preview) + { + var truncatedString = entry.String.Length > 50 ? entry.String.Substring(0, 50) + "..." : entry.String; + previewMessage += $"• {entry.RecNo} | {entry.Label} | {truncatedString}\n"; + } + + if (importResult.ImportedEntries.Count > 5) + { + previewMessage += $"... and {importResult.ImportedEntries.Count - 5} more records.\n"; + } + + previewMessage += "\nThis operation will:\n" + + "• Replace existing texts with same Label\n" + + "• Add new records if Label doesn't exist\n" + + "• Keep existing records not in the file\n\n" + + "Do you want to continue with the import?"; + + var confirmResult = MessageBox.Show(previewMessage, "Confirm Import", + MessageBoxButtons.YesNo, MessageBoxIcon.Question); + + if (confirmResult == DialogResult.Yes) + { + ApplyImportedData(importResult.ImportedEntries, page, categoryName); + + MessageBox.Show($"Import completed successfully!\n\n{importResult.Message}", + "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); + + // Atualizar a interface + DisplayEntries(page); + toolStripStatusLabel.Text = $"Import completed: {importResult.ImportedEntries.Count} records processed"; + } + else + { + toolStripStatusLabel.Text = "Import cancelled by user"; + } + } + catch (Exception ex) + { + MessageBox.Show($"Error during import: {ex.Message}", "Error", + MessageBoxButtons.OK, MessageBoxIcon.Error); + toolStripStatusLabel.Text = "Error during import"; + } + finally + { + this.Cursor = Cursors.Default; + } + } + + private void ApplyImportedData(List entries, RTextPageBase page, string categoryName) + { + int updatedCount = 0; + int addedCount = 0; + + foreach (var entry in entries) + { + if (_isUiFolderProject) + { + // Apply to all locales if it's a folder project + foreach (var rt in _rTexts) + { + try + { + var rtPage = rt.RText.GetPages()[categoryName]; + if (rtPage.PairExists(entry.Label)) + { + // For existing records, preserve original ID and only update the value + var existingPair = rtPage.PairUnits[entry.Label]; + rtPage.EditRow(existingPair.ID, entry.Label, entry.String); + updatedCount++; + } + else + { + // For new records, use a unique ID based on last ID + 1 + int newId = rtPage.PairUnits.Count > 0 ? rtPage.GetLastId() + 1 : 1; + rtPage.AddRow(newId, entry.Label, entry.String); + addedCount++; + } + } + catch (Exception ex) + { + Console.WriteLine($"Error applying data to locale {rt.LocaleCode}: {ex.Message}"); + } + } + } + else + { + // Apply only to current file + if (page.PairExists(entry.Label)) + { + // For existing records, preserve original ID and only update the value + var existingPair = page.PairUnits[entry.Label]; + page.EditRow(existingPair.ID, entry.Label, entry.String); + updatedCount++; + } + else + { + // For new records, use a unique ID based on last ID + 1 + int newId = page.PairUnits.Count > 0 ? page.GetLastId() + 1 : 1; + page.AddRow(newId, entry.Label, entry.String); + addedCount++; + } + } + } + + var summary = ""; + if (_isUiFolderProject) + { + summary = $"Aplicado para {_rTexts.Count} locales: "; + } + + summary += $"{updatedCount} registros atualizados, {addedCount} registros adicionados"; + + Console.WriteLine($"Importação completada: {summary}"); + } + + private void ExportCategoryToCsv_Click(object sender, EventArgs e) + { + // Verificar se há uma categoria selecionada + if (listViewPages.SelectedItems.Count <= 0 || listViewPages.SelectedItems[0] == null) + { + MessageBox.Show("Please select a category first.", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + var selectedItem = listViewPages.SelectedItems[0]; + var page = (RTextPageBase)selectedItem.Tag; + var categoryName = page.Name; + + var saveFileDialog = new SaveFileDialog + { + Filter = "CSV Files (*.csv)|*.csv|All files (*.*)|*.*", + Title = "Export category to CSV", + FileName = $"{categoryName}_export.csv", + CheckPathExists = true + }; + + if (saveFileDialog.ShowDialog(this) == DialogResult.OK) + { + ExportCategoryData(saveFileDialog.FileName, page, categoryName); + } + } + + private void ExportCategoryData(string filePath, RTextPageBase page, string categoryName) + { + try + { + // Mostrar cursor de espera + this.Cursor = Cursors.WaitCursor; + toolStripStatusLabel.Text = "Exporting category data..."; + + // Converter os dados da página para o formato de exportação + var exportEntries = new List(); + + foreach (var pair in page.PairUnits.Values.OrderBy(p => p.ID)) + { + exportEntries.Add(new ImportedEntry + { + RecNo = pair.ID, + Label = pair.Label, + String = pair.Value + }); + } + + // Exportar para CSV + bool success = ExcelImporter.ExportToCsv(filePath, exportEntries); + + if (success) + { + var successMessage = $"Category '{categoryName}' exported successfully!\n\n" + + $"File: {Path.GetFileName(filePath)}\n" + + $"Records exported: {exportEntries.Count}\n" + + $"Format: RecNo,Label,String"; + + MessageBox.Show(successMessage, "Export Successful", + MessageBoxButtons.OK, MessageBoxIcon.Information); + + toolStripStatusLabel.Text = $"Export completed: {exportEntries.Count} records exported to {Path.GetFileName(filePath)}"; + } + else + { + MessageBox.Show("Failed to export category data. Please check the file path and try again.", + "Export Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + toolStripStatusLabel.Text = "Export failed"; + } + } + catch (Exception ex) + { + MessageBox.Show($"Error during export: {ex.Message}", "Error", + MessageBoxButtons.OK, MessageBoxIcon.Error); + toolStripStatusLabel.Text = "Error during export"; + } + finally + { + this.Cursor = Cursors.Default; + } + } + + #endregion } } diff --git a/GT.RText/Properties/AssemblyInfo.cs b/GT.RText/Properties/AssemblyInfo.cs index 003ad58..fcc2454 100644 --- a/GT.RText/Properties/AssemblyInfo.cs +++ b/GT.RText/Properties/AssemblyInfo.cs @@ -9,8 +9,8 @@ [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("eventHorizon")] -[assembly: AssemblyProduct("GT.RText")] -[assembly: AssemblyCopyright("Copyright © xfileFIN 2022")] +[assembly: AssemblyProduct("GT.RText Enhanced Edition")] +[assembly: AssemblyCopyright("Original © xfileFIN 2022 | Enhanced by Derek W 2025")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.9.3.0")] -[assembly: AssemblyFileVersion("1.9.3.0")] +[assembly: AssemblyVersion("1.9.4.0")] +[assembly: AssemblyFileVersion("1.9.4.0")] diff --git a/GT.RText/RowEditor.Designer.cs b/GT.RText/RowEditor.Designer.cs index 3ca60c7..90e9b9a 100644 --- a/GT.RText/RowEditor.Designer.cs +++ b/GT.RText/RowEditor.Designer.cs @@ -141,7 +141,7 @@ private void InitializeComponent() this.Controls.Add(this.label_id); this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow; this.Name = "RowEditor"; - this.Text = "Editor"; + this.Text = "GT.RText - Row Editor"; ((System.ComponentModel.ISupportInitialize)(this.numericUpDown_id)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); diff --git a/GT.RText/RowEditor.cs b/GT.RText/RowEditor.cs index ab62ff1..9758d54 100644 --- a/GT.RText/RowEditor.cs +++ b/GT.RText/RowEditor.cs @@ -1,4 +1,6 @@ using System; +using System.Drawing; +using System.IO; using System.Windows.Forms; namespace GT.RText @@ -15,6 +17,12 @@ public partial class RowEditor : Form public RowEditor() { InitializeComponent(); + + // Aplicar tema escuro + DarkTheme.ApplyDarkTheme(this); + + // Carregar ícone se existir + LoadIcon(); } public RowEditor(bool isWithoutId, bool isUiProject) @@ -26,6 +34,12 @@ public RowEditor(bool isWithoutId, bool isUiProject) _isUiProject = isUiProject; if (isUiProject) applyAllLocalesCheckBox.Visible = true; + + // Aplicar tema escuro + DarkTheme.ApplyDarkTheme(this); + + // Carregar ícone se existir + LoadIcon(); } public RowEditor(int id, string label, string data, bool isUiProject) @@ -44,6 +58,28 @@ public RowEditor(int id, string label, string data, bool isUiProject) richTextBox_data.Text = data; HandleId(id); + + // Aplicar tema escuro + DarkTheme.ApplyDarkTheme(this); + + // Carregar ícone se existir + LoadIcon(); + } + + private void LoadIcon() + { + try + { + string iconPath = Path.Combine(Application.StartupPath, "app.ico"); + if (File.Exists(iconPath)) + { + this.Icon = new Icon(iconPath); + } + } + catch + { + // Ignorar se não conseguir carregar o ícone + } } private void HandleId(int id) diff --git a/GT.RText/app.ico b/GT.RText/app.ico new file mode 100644 index 0000000000000000000000000000000000000000..0d17c9a4c49c439e2902bf4122fd9336d7cf3300 GIT binary patch literal 4286 zcmeHK>rb0y6z_$uExqtkN`aM@((Bvn+Hx5ySPOO%NJXX?{9@(@4NfCpbRmmzSv1a( zA-6$*fP@$lD@#bkD-ku(4;y&-mwuk}bnRN5v*CmKp>2Ne^StM|{C>|l=Q&Lk3MGAA zE(Jdoc}bzLDHIBsKqf_oz;^MWF;A?3fhVsZ53dE|s3&f&i|pU^^))Ok z%+p?Z%*P7C-u(>MuFW7CjbLeMk+6idwKW_Z9AIv4uJnF*c!;B;BU-Z|^s`UQ zX7eL8Jk#ODeBU5s@dI4O?>@-Q79(d_lNe zE^yqPPNx8CFUFt8I0vj&D>RzI*@_(88tA+;=g#B(?g7d>eTiSC)WT`MiLn0=LEqO1 z`M-iix`a;0ys(7>-$3?!4x^#Bw5ELDYWr>V27K6?#s@u5z^1RS*sIm{DiV=j5Dyxz3<(^_?UyhpxY?a$&BIfgT;hk073Za~2E$patU7ra)> z1nI~V|B28Q4So+peeBfyTDlk0kuIzd|NMnPq}$q|EUSk$>r2G@v*6+ww70#D4*QLg zCf3o?eEHP;?4zgKUAU8aN`Pa*8Z;`E*i)s_!))q@web?+Ihc)oXlfiIJ@*KO72cfp4MLc~8yHd-u26 zu|T}czf9nGurIhKW$hh^G+N;?OK=*^Flr2NIc`#J{(z|booG89x6sxyMYvLi*I_bN zcR$(R&r%)Ydr;KCE$d*vERcP!FN53XGa^qVy&XE8pU&;08jysqa}&wPZz3=F&N7nG z-{E%NhEn<0x>N4YVxMz=m~w&7&AjY~KZ7-~pF)ifYbCF@NV!r@a9$K}OttC&+B43o zW1~_ksSaCkB^1X;RFheU@Ijdtejj)FD)!WBJ18f~>XKdhDm|9TFP I%m2I2AFmh_0ssI2 literal 0 HcmV?d00001 diff --git a/GT.RText/icon.txt b/GT.RText/icon.txt new file mode 100644 index 0000000..4380b8f --- /dev/null +++ b/GT.RText/icon.txt @@ -0,0 +1 @@ +AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DDw8PAw8PDwMPDw8DAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= diff --git a/GT.RText/packages.config b/GT.RText/packages.config index aaf6587..902666d 100644 --- a/GT.RText/packages.config +++ b/GT.RText/packages.config @@ -1,5 +1,6 @@  + \ No newline at end of file diff --git a/GT.Shared/ExcelImporter.cs b/GT.Shared/ExcelImporter.cs new file mode 100644 index 0000000..83bd80e --- /dev/null +++ b/GT.Shared/ExcelImporter.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace GT.Shared +{ + public class ExcelImportResult + { + public bool Success { get; set; } + public string Message { get; set; } + public List ImportedEntries { get; set; } = new List(); + } + + public class ImportedEntry + { + public int RecNo { get; set; } + public string Label { get; set; } + public string String { get; set; } + } + + public static class ExcelImporter + { + /// + /// Imports data from a CSV file with format: RecNo, Label, String + /// + public static ExcelImportResult ImportFromCsv(string filePath) + { + var result = new ExcelImportResult(); + + try + { + if (!File.Exists(filePath)) + { + result.Message = "Arquivo não encontrado."; + return result; + } + + var lines = File.ReadAllLines(filePath); + + if (lines.Length < 2) + { + result.Message = "The file must contain at least one header line and one data line."; + return result; + } + + // Check header (first line) + var headerLine = lines[0].ToLower(); + if (!headerLine.Contains("recno") || !headerLine.Contains("label") || !headerLine.Contains("string")) + { + result.Message = "The file must have columns in order: RecNo, Label, String.\n" + + $"Header found: '{lines[0]}'"; + return result; + } + + // Process data lines (skip header) + for (int i = 1; i < lines.Length; i++) + { + var line = lines[i].Trim(); + + // Pular linhas vazias + if (string.IsNullOrEmpty(line)) + continue; + + var parts = ParseCsvLine(line); + + if (parts.Length < 3) + { + Console.WriteLine($"Linha {i + 1}: Formato inválido '{line}', ignorando linha."); + continue; + } + + // Clean and process fields + var recNoText = parts[0].Trim().Trim('"'); // Remove extra quotes from Excel + var label = parts[1].Trim().Trim('"'); // Remove extra quotes from Excel + var stringValue = parts[2].Trim().Trim('"'); // Remove extra quotes from Excel + + // If there are still quotes in the middle of text, restore them correctly + label = label.Replace("\"\"", "\""); + stringValue = stringValue.Replace("\"\"", "\""); + + if (int.TryParse(recNoText, out int recNo)) + { + result.ImportedEntries.Add(new ImportedEntry + { + RecNo = recNo, + Label = label, + String = stringValue + }); + } + else + { + Console.WriteLine($"Linha {i + 1}: RecNo inválido '{recNoText}', ignorando linha."); + } + } + + if (result.ImportedEntries.Count == 0) + { + result.Message = "Nenhum registro válido foi encontrado no arquivo."; + return result; + } + + result.Success = true; + result.Message = $"Importados {result.ImportedEntries.Count} registros com sucesso."; + } + catch (Exception ex) + { + result.Success = false; + result.Message = $"Erro ao importar arquivo: {ex.Message}"; + } + + return result; + } + + /// + /// Parses a CSV line considering quotes and commas inside fields + /// Compatible with Excel format that adds quotes to all fields + /// + private static string[] ParseCsvLine(string line) + { + var result = new List(); + var current = ""; + bool inQuotes = false; + + for (int i = 0; i < line.Length; i++) + { + char c = line[i]; + + if (c == '"') + { + if (inQuotes && i + 1 < line.Length && line[i + 1] == '"') + { + // Two consecutive quotes = one literal quote + current += '"'; + i++; // Skip next quote + } + else + { + // Toggle quote state + inQuotes = !inQuotes; + } + } + else if (c == ',' && !inQuotes) + { + // Comma outside quotes = field separator + result.Add(current.Trim('"')); // Remove aspas extras do Excel + current = ""; + } + else + { + current += c; + } + } + + // Add last field, removing extra quotes + result.Add(current.Trim('"')); + + return result.ToArray(); + } + + /// + /// Creates a sample CSV file for the user + /// + public static void CreateSampleCsv(string filePath) + { + var sampleContent = @"RecNo,Label,String +1,the exact same label must be here,This is an example string for entry 1. +2,the exact same label must be here,This is an example string for entry 2."; + + File.WriteAllText(filePath, sampleContent); + } + + /// + /// Exports category entries to CSV format + /// + public static bool ExportToCsv(string filePath, List entries) + { + try + { + var csvContent = "RecNo,Label,String\n"; + + foreach (var entry in entries) + { + // Escape quotes in the data by doubling them + var label = entry.Label.Replace("\"", "\"\""); + var stringValue = entry.String.Replace("\"", "\"\""); + + // Add quotes around fields that contain commas, quotes, or newlines + if (label.Contains(",") || label.Contains("\"") || label.Contains("\n")) + label = $"\"{label}\""; + + if (stringValue.Contains(",") || stringValue.Contains("\"") || stringValue.Contains("\n")) + stringValue = $"\"{stringValue}\""; + + csvContent += $"{entry.RecNo},{label},{stringValue}\n"; + } + + File.WriteAllText(filePath, csvContent); + return true; + } + catch (Exception) + { + return false; + } + } + } +} diff --git a/README.md b/README.md index 87db355..e11d495 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,83 @@ -# GT.RText -Supports editing RT03, RT04, RT05 and 50TR files. +# GT.RText v1.9.4 - Enhanced Edition +==================================== -![Preview](https://repository-images.githubusercontent.com/284490993/b578fa00-d4f2-11ea-93f0-e4cbfbbdfadd) +## IMPORTANT NOTICE +This is a **MODIFIED** version of the original GT.RText by xfileFIN +Original author: **xfileFIN** © 2022 +Enhanced by: **Derek W** after requested by a friend - 2025 +This version includes additional features not present in the original. -### Credits: +--- -[Nenkai](https://github.com/Nenkai)
+## OVERVIEW +Gran Turismo RText Editor with enhanced features including: +- CSV Import/Export functionality for bulk editing +- Pure black dark theme with dark title bar +- Custom icon support +- English interface and improved usability -- solving which cipher was being called to decipher the RT05 and 50TR strings.
-- Adding support for editing whole UI folder
+--- -[flatz](https://github.com/flatz/gttool)
+## CREDITS +• Original GT.RText: xfileFIN - 2022 +• Enhanced Edition: Derek W - 2025 +• Additional Features: CSV Import/Export, Dark Theme, UI Improvements -- for reverse engineering the said cipher (Salsa20).
+--- + +## SYSTEM REQUIREMENTS +- Windows 10/11 +- .NET Framework 4.7.2 or higher +- 50MB free disk space + +--- + +## INSTALLATION +1. Extract all files to a folder of your choice +2. Ensure all DLL files are in the same folder as GT.RText.exe +3. Run GT.RText.exe + +--- + +## FEATURES +- **Dark Theme:** Pure black interface with dark window title bar +- **CSV Import:** Import text entries for bulk editing from CSV files +- **CSV Export:** Export category data to CSV format for backup/editing +- **Icon Support:** Custom application icon (app.ico) +- **English Interface:** Fully translated interface and comments +- **Enhanced Compatibility:** Works with Excel CSV formats + +--- + +## USAGE +1. Open a Gran Turismo RText file (`.rt03`, `.rt04`, `.rt05`, `._50tr`) +2. Right-click on a category to access Import/Export options +3. Use "Import this category from CSV" for bulk updates +4. Use "Export this category to CSV" for data backup +5. CSV format: `RecNo,Label,String` (Excel compatible) + +--- + +## CSV IMPORT FORMAT +``` +RecNo,Label,String +1,LABEL_NAME,Display Text Here +2,ANOTHER_LABEL,Another Text Here +``` + +--- + +## TROUBLESHOOTING +- If the app doesn't start, install .NET Framework 4.7.2 +- Ensure all DLL files are in the same folder +- Run as Administrator if file access issues occur + +--- + +## VERSION HISTORY +Build Date: August 26, 2025 +Build Version: v1.9.4 +Original Author: xfileFIN © 2022 +Enhanced by: Derek W © 2025 +Built with: Visual Studio 2022, MSBuild 17.13 From 0d59c99d7e0735e93ca9ea039462203e2a76e260 Mon Sep 17 00:00:00 2001 From: "Derek W." Date: Tue, 26 Aug 2025 15:30:36 -0300 Subject: [PATCH 2/2] Revise README for clarity and enhanced features Updated README to clarify modifications and enhance wording. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e11d495..90ec990 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ # GT.RText v1.9.4 - Enhanced Edition ==================================== + GT RText_91r9j1yscx + + ## IMPORTANT NOTICE This is a **MODIFIED** version of the original GT.RText by xfileFIN Original author: **xfileFIN** © 2022 @@ -11,7 +14,7 @@ This version includes additional features not present in the original. --- ## OVERVIEW -Gran Turismo RText Editor with enhanced features including: +Gran Turismo RText Editor with enhanced features, including: - CSV Import/Export functionality for bulk editing - Pure black dark theme with dark title bar - Custom icon support