diff --git a/TagHierarchyManager.sln b/TagHierarchyManager.sln
index 9ea0ac1..6d3e6ea 100644
--- a/TagHierarchyManager.sln
+++ b/TagHierarchyManager.sln
@@ -9,10 +9,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{397693A2
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagHierarchyManager.Tests", "Tests\TagHierarchyManager.Tests.csproj", "{F26B4A2D-0389-4FE8-8B0D-E491B354AC83}"
EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UI", "UI", "{9666D8FC-785C-4F34-B224-21636441C508}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagHierarchyManager.UI.TerminalUI", "UI\Terminal\TagHierarchyManager.UI.TerminalUI.csproj", "{FC98B5BA-1E3D-42F2-8A32-CDDAD1FEDF72}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -30,13 +26,8 @@ Global
{F26B4A2D-0389-4FE8-8B0D-E491B354AC83}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F26B4A2D-0389-4FE8-8B0D-E491B354AC83}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F26B4A2D-0389-4FE8-8B0D-E491B354AC83}.Release|Any CPU.Build.0 = Release|Any CPU
- {FC98B5BA-1E3D-42F2-8A32-CDDAD1FEDF72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {FC98B5BA-1E3D-42F2-8A32-CDDAD1FEDF72}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {FC98B5BA-1E3D-42F2-8A32-CDDAD1FEDF72}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {FC98B5BA-1E3D-42F2-8A32-CDDAD1FEDF72}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F26B4A2D-0389-4FE8-8B0D-E491B354AC83} = {397693A2-4BD9-4EA2-B70E-84B0B4DD2BF9}
- {FC98B5BA-1E3D-42F2-8A32-CDDAD1FEDF72} = {9666D8FC-785C-4F34-B224-21636441C508}
EndGlobalSection
EndGlobal
diff --git a/UI/Terminal/Program.cs b/UI/Terminal/Program.cs
deleted file mode 100644
index 653e9b3..0000000
--- a/UI/Terminal/Program.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using Serilog;
-using TagHierarchyManager.Models;
-using TagHierarchyManager.UI.TerminalUI.Views;
-using Terminal.Gui.App;
-
-namespace TagHierarchyManager.UI.TerminalUI;
-
-///
-/// The class that the application will execute at first.
-///
-internal static class Program
-{
- ///
- /// A logger for debugging purposes that logs to any compatible .NET debugger, using Serilog.
- ///
- public static readonly ILogger Logger = new LoggerConfiguration()
- .MinimumLevel.Debug()
- .WriteTo.Debug()
- .CreateLogger();
-
- ///
- /// Gets the top-level view, which also encapsulates the main window of the application.
- ///
- internal static readonly TopView TopView = new();
-
- ///
- /// Gets the current database associated with the running process of the application.
- ///
- internal static TagDatabase? CurrentDatabase = null!;
-
- ///
- /// Gets the main editor window of the application, essentially acting as a shortcut.
- ///
- internal static EditorWindow MainView => TopView.Window;
-
- ///
- /// The method the .NET runtime will use to execute the application.
- ///
- public static void Main()
- {
- try
- {
- Application.Init();
- Application.Run(TopView);
- }
- catch (Exception ex)
- {
- Logger.Fatal(ex, "FATAL ERROR WAS CAUGHT.");
- }
- finally
- {
- Application.Shutdown();
- }
- }
-}
\ No newline at end of file
diff --git a/UI/Terminal/Properties/PublishProfiles/Linux.pubxml b/UI/Terminal/Properties/PublishProfiles/Linux.pubxml
deleted file mode 100644
index 6bb4f1b..0000000
--- a/UI/Terminal/Properties/PublishProfiles/Linux.pubxml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
- linux-x64
- true
- true
- full
- true
- none
- false
-
-
diff --git a/UI/Terminal/Properties/PublishProfiles/Windows.pubxml b/UI/Terminal/Properties/PublishProfiles/Windows.pubxml
deleted file mode 100644
index 4e8b01c..0000000
--- a/UI/Terminal/Properties/PublishProfiles/Windows.pubxml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
- win-x64
- true
- true
- full
- true
- none
- false
-
-
diff --git a/UI/Terminal/Services/TagDatabaseService.cs b/UI/Terminal/Services/TagDatabaseService.cs
deleted file mode 100644
index 4796970..0000000
--- a/UI/Terminal/Services/TagDatabaseService.cs
+++ /dev/null
@@ -1,266 +0,0 @@
-using TagHierarchyManager.Common;
-using TagHierarchyManager.Exporters;
-using TagHierarchyManager.Importers;
-using TagHierarchyManager.Models;
-using TagHierarchyManager.UI.TerminalUI.Views;
-using Terminal.Gui.App;
-
-namespace TagHierarchyManager.UI.TerminalUI.Services;
-
-///
-/// Handles interaction between the and the UI.
-///
-public static class TagDatabaseService
-{
- ///
- /// An event thrown when the database initialisation has started.
- ///
- public static event EventHandler DatabaseInitialising = delegate { };
-
- ///
- /// An event thrown when the database export process has completed, storing a string with it pointing to the
- /// export's file path.
- ///
- public static event EventHandler ExportCompleted = delegate { };
-
- ///
- /// Thrown when an exception has occurred while exporting, storing a string with it
- /// specifying the error.
- ///
- public static event EventHandler ExportError = delegate { };
-
- ///
- /// Thrown when the database export process has started.
- ///
- public static event EventHandler ExportStarted = delegate { };
-
- ///
- /// Thrown when an exception has occurred while processing data to import, storing a string with it
- /// specifying the error.
- ///
- public static event EventHandler ImportError = delegate { };
-
- ///
- /// Thrown when the initialisation has completed.
- ///
- public static event EventHandler InitialisationComplete = delegate { };
-
- ///
- /// Thrown when an exception has occurred while attempting to initialise the database, storing a string with it
- /// specifying the error.
- ///
- public static event EventHandler InitialisationError = delegate { };
-
- ///
- /// Thrown when the search process has finished, storing with it the number of Tags found.
- ///
- public static event EventHandler> SearchFinished = delegate { };
-
- ///
- /// Thrown when a new Tag has been added, storing the newly created Tag object with it.
- ///
- public static event EventHandler TagAdded = delegate { };
-
- ///
- /// Thrown when a Tag has been deleted, storing the ID and name of the Tag with it.
- ///
- public static event EventHandler<(int, string)> TagDeleted = delegate { };
-
- ///
- /// Thrown when a Tag has been saved, regardless of whether it was new or not, storing the saved Tag object
- /// with it.
- ///
- public static event EventHandler TagSaved = delegate { };
-
- // should this be generic and inside the Core?
- // not yet, we'll see when i get to adding the avalonia ui.
-
- ///
- /// Throws a save dialog to the user, and starts the initialisation process.
- ///
- public static void CreateDatabase((string filePath, bool userAllowedOverwrite) newDatabase)
- {
- StartDatabaseInit(newDatabase.filePath, newDatabase.userAllowedOverwrite);
- }
-
- public static async Task DeleteTag(Tag tag)
- {
- string tagName = tag.Name;
- int tagId = tag.Id;
- if (Program.CurrentDatabase != null) await Program.CurrentDatabase.DeleteTag(tag.Id);
- TagDeleted.Invoke(null, (tagId, tagName));
-
- }
-
- ///
- /// Throws a load dialog to the user, and starts the initialisation process.
- ///
- public static void PromptDatabaseLoad()
- {
- string? newDatabasePath = StandardDialogs.DatabaseOpenDialog();
- if (newDatabasePath is null) return;
-
- StartDatabaseInit(newDatabasePath);
- }
-
- public static void SearchTags(string query, TagDatabaseSearchMode mode, bool searchAliases)
- {
- if (Program.CurrentDatabase is null) throw new InvalidOperationException(ErrorMessages.DbNotInitialised);
-
- List tags = searchAliases
- ? Program.CurrentDatabase.SearchWithAliases(query, mode)
- : Program.CurrentDatabase.Search(query, mode);
- SearchFinished.Invoke(Program.CurrentDatabase, tags.OrderBy(t => t.Name).ToList());
- }
-
- ///
- /// Starts the database initialisation.
- ///
- /// The file path of the database to create or load.
- ///
- /// Whether to overwrite the database file on filePath.
- /// Null implies database load - true/false implies database creation.
- ///
- /// Optional path to a file to import into the new database (used on creation).
- public static void StartDatabaseInit(
- string filePath,
- bool? overwriteOnCreation = null,
- string? importedFilePath = null)
- {
- ClearMainWindowData();
-
- TagDatabase db = new();
- SubscribeToEvents(db);
-
- _ = Task.Run(async () =>
- {
- try
- {
- Dictionary? tagsToImport = null;
- if (overwriteOnCreation.HasValue)
- {
- if (!string.IsNullOrEmpty(importedFilePath))
- try
- {
- tagsToImport = await PickImporterFromFileExt(importedFilePath)
- .ImportFromFileAsync(importedFilePath);
- }
- catch (Exception ex)
- {
- Application.Invoke(() => ImportError.Invoke(null, ex.Message));
- return;
- }
-
- DatabaseInitialising.Invoke(null, EventArgs.Empty);
- await db.CreateAsync(filePath, overwriteOnCreation.Value,
- tagsToImport);
- }
- else
- {
- DatabaseInitialising.Invoke(null, EventArgs.Empty);
- await db.LoadAsync(filePath);
- }
- }
- catch (Exception ex)
- {
- InitialisationError.Invoke(db, ex.Message);
- UnsubscribeFromEvents(db);
- }
- });
- }
-
- public static void StartExportProcess()
- {
- if (Program.CurrentDatabase is null) return;
- (string filePath, bool userAllowedOverwrite)? saveLocation = StandardDialogs.ExportSaveDialog();
- if (saveLocation is null) return;
- _ = Task.Run(async () =>
- {
- ExportStarted.Invoke(null, EventArgs.Empty);
- await ExportToFile(saveLocation.Value);
- });
- }
-
- public static async Task WriteTagToDatabase(Tag tag)
- {
- bool newTag = tag.Id == 0;
- if (Program.CurrentDatabase != null) await Program.CurrentDatabase.WriteTagToDatabase(tag);
-
- if (newTag) TagAdded.Invoke(Program.CurrentDatabase, tag);
- TagSaved.Invoke(Program.CurrentDatabase, tag);
- }
-
- ///
- /// Clears the data associated with the main window, prompting the user if unsaved changes were detected.
- ///
- /// True if the data has been cleared.
- private static void ClearMainWindowData()
- {
- if (Program.CurrentDatabase is null) return;
- UnsubscribeFromEvents(Program.CurrentDatabase);
- Program.CurrentDatabase.Close();
- }
-
- private static async Task ExportToFile((string filePath, bool userAllowedOverwrite) exportLocation)
- {
- if (Program.CurrentDatabase is null) return;
- IExporter exporter = PickExporterFromFileExt(exportLocation.filePath);
- try
- {
- string export = exporter.ExportDatabase(Program.CurrentDatabase);
- if (exportLocation.userAllowedOverwrite && File.Exists(exportLocation.filePath))
- File.Delete(exportLocation.filePath);
- await File.WriteAllTextAsync(exportLocation.filePath, export);
- ExportCompleted.Invoke(null, exportLocation.filePath);
- }
- catch (Exception ex)
- {
- ExportError.Invoke(exportLocation.filePath, ex.Message);
- }
- }
-
- private static void OnInitialisationComplete(object? sender, EventArgs e)
- {
- Application.Invoke(() =>
- {
- Program.Logger.Debug("[LoadingDialog.OnInitialised] Event has been caught");
- Program.Logger.Information("[LoadingDialog.OnInitialised] Sender: {@Sender}", sender);
- if (sender is not TagDatabase db) return;
-
- Program.CurrentDatabase = db;
-
- InitialisationComplete.Invoke(db, EventArgs.Empty);
- });
- }
-
- private static IExporter PickExporterFromFileExt(string path)
- {
- string fileExt = Path.GetExtension(path);
-
- // ReSharper disable once ConvertIfStatementToReturnStatement
- if (fileExt == FileTypes.MusicBeeTagHierarchyTemplate.FileExtension) return new MusicBeeTagHierarchyExporter();
-
- throw new NotSupportedException($"File extension '{fileExt}' is not supported.");
- }
-
- private static Importer PickImporterFromFileExt(string path)
- {
- string fileExt = Path.GetExtension(path);
-
- // if more importers are added, convert this to a switch statement based on file extension.
- // ReSharper disable once ConvertIfStatementToReturnStatement
- if (fileExt == FileTypes.MusicBeeTagHierarchyTemplate.FileExtension) return new MusicBeeTagHierarchyImporter();
-
- throw new NotSupportedException($"File extension '{fileExt}' is not supported.");
- }
-
- private static void SubscribeToEvents(TagDatabase db)
- {
- db.InitialisationComplete += OnInitialisationComplete;
- }
-
- private static void UnsubscribeFromEvents(TagDatabase db)
- {
- db.InitialisationComplete -= OnInitialisationComplete;
- }
-}
\ No newline at end of file
diff --git a/UI/Terminal/TagHierarchyManager.UI.TerminalUI.csproj b/UI/Terminal/TagHierarchyManager.UI.TerminalUI.csproj
deleted file mode 100644
index a51b83c..0000000
--- a/UI/Terminal/TagHierarchyManager.UI.TerminalUI.csproj
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-
- Exe
- net9.0
- enable
- enable
- portable
- TagHierarchyManager.UI.TerminalUI
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/UI/Terminal/TagHierarchyManager.UI.TerminalUI.csproj.DotSettings b/UI/Terminal/TagHierarchyManager.UI.TerminalUI.csproj.DotSettings
deleted file mode 100644
index dcf9c9b..0000000
--- a/UI/Terminal/TagHierarchyManager.UI.TerminalUI.csproj.DotSettings
+++ /dev/null
@@ -1,7 +0,0 @@
-
- True
- True
\ No newline at end of file
diff --git a/UI/Terminal/Views/Dialogs/DbSettingsDialog.cs b/UI/Terminal/Views/Dialogs/DbSettingsDialog.cs
deleted file mode 100644
index 7876294..0000000
--- a/UI/Terminal/Views/Dialogs/DbSettingsDialog.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using Terminal.Gui.App;
-using Terminal.Gui.ViewBase;
-using Terminal.Gui.Views;
-
-namespace TagHierarchyManager.UI.TerminalUI.Views;
-
-///
-/// A for handling TagDatabase settings.
-///
-internal class DbSettingsDialog : UserInputDialog
-{
- private readonly TextField defaultTagBindingField = new()
- {
- X = 0,
- Width = Dim.Fill(),
- };
-
- ///
- /// Initializes a new instance of the class.
- ///
- public DbSettingsDialog()
- {
- this.InitialiseUI();
- }
-
- private void InitialiseUI()
- {
- this.Title = $"Settings - {Program.CurrentDatabase!.Name}";
- this.Width = 70;
- this.Height = 12;
-
- Label schemaVersionLabel = new()
- {
- X = 0,
- Y = 0,
- Title = $"Database version: {Program.CurrentDatabase.Version}",
- };
-
- Label defaultTagBindingLabel = new()
- {
- X = 0,
- Y = Pos.Bottom(schemaVersionLabel) + 1,
- Title = "Default tag binding (separated by semi-colon):",
- };
- this.defaultTagBindingField.Y = Pos.Bottom(defaultTagBindingLabel);
- this.defaultTagBindingField.Text = string.Join(';', Program.CurrentDatabase.DefaultTagBindings);
- this.OkButton.Accepting += this.OkButton_OnAccepting;
-
- this.Add(schemaVersionLabel, defaultTagBindingLabel, this.defaultTagBindingField);
- }
-
- private void OkButton_OnAccepting(object? sender, EventArgs e)
- {
- string newDefaultTagBindings = this.defaultTagBindingField.Text.Replace("::", string.Empty);
- Program.CurrentDatabase!.DefaultTagBindings =
- newDefaultTagBindings.Split(
- ';',
- StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)
- .ToList();
- Program.Logger.Debug("{@TagBindings}", Program.CurrentDatabase.DefaultTagBindings);
- Application.RequestStop();
- }
-}
\ No newline at end of file
diff --git a/UI/Terminal/Views/Dialogs/ImportDialog.cs b/UI/Terminal/Views/Dialogs/ImportDialog.cs
deleted file mode 100644
index a840ebf..0000000
--- a/UI/Terminal/Views/Dialogs/ImportDialog.cs
+++ /dev/null
@@ -1,234 +0,0 @@
-using TagHierarchyManager.Models;
-using TagHierarchyManager.UI.TerminalUI.Services;
-using Terminal.Gui.App;
-using Terminal.Gui.Drivers;
-using Terminal.Gui.Input;
-using Terminal.Gui.ViewBase;
-using Terminal.Gui.Views;
-
-namespace TagHierarchyManager.UI.TerminalUI.Views;
-
-///
-/// A for importing MusicBee tag hierarchy files.
-///
-internal class ImportDialog : UserInputDialog
-{
- private (string filePath, bool userAllowedOverwrite)? chosenDatabaseLocation;
- private Label errorLabel = null!;
- private View importingView = null!;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public ImportDialog()
- {
- this.InitialiseUI();
- }
-
- ///
- /// Gets the object representing the current database to save to.
- ///
- internal (string filePath, bool userAllowedOverwrite)? CurrentDatabase => this.chosenDatabaseLocation;
-
- ///
- /// Gets the containing the file path to where the new should be
- /// saved.
- ///
- private TextField DatabaseField { get; set; } = null!;
-
- ///
- /// Gets the containing the file path to the target file for import.
- ///
- private TextField ImportedFileField { get; set; } = null!;
-
- private void databaseBrowseButton_OnAccepting(object? sender, CommandEventArgs e)
- {
- (string filePath, bool userAllowedOverwrite)? dbFile = StandardDialogs.NewDatabaseSaveDialog();
- if (dbFile.HasValue)
- {
- this.chosenDatabaseLocation = dbFile;
- this.DatabaseField.Text = dbFile.Value.filePath;
- this.DatabaseField.CursorPosition = dbFile.Value.filePath.Length;
-
- if (!string.IsNullOrEmpty(this.ImportedFileField.Text)) this.OkButton.Enabled = true;
- }
-
- e.Handled = true;
- }
-
- private void InitialiseUI()
- {
- this.Title = "Import file";
- this.Arrangement = ViewArrangement.Movable;
- this.Width = 70;
- this.Height = 12;
-
- Label tagHierarchyLabel = new()
- {
- X = 0,
- Y = 0,
- Title = "File to import:",
- };
-
- const string browseTitle = "Browse";
-
- this.ImportedFileField = new TextField
- {
- Y = Pos.Bottom(tagHierarchyLabel),
- Width = Dim.Fill() ! - (browseTitle.Length + 5),
- CanFocus = false,
- CursorVisibility = CursorVisibility.Invisible,
- };
-
- Button importFileBrowseButton = new()
- {
- X = Pos.Right(this.ImportedFileField),
- Y = Pos.Bottom(tagHierarchyLabel),
- Width = Dim.Fill(),
- Title = browseTitle,
- };
- importFileBrowseButton.Accepting += this.OnImportFileBrowseClick;
-
- Label databaseLabel = new()
- {
- X = 0,
- Y = Pos.Bottom(this.ImportedFileField) + 1,
- Title = "Database to save to:",
- };
-
- this.DatabaseField = new TextField
- {
- Y = Pos.Bottom(databaseLabel),
- Width = Dim.Fill() ! - (browseTitle.Length + 5),
- CanFocus = false,
- CursorVisibility = CursorVisibility.Invisible,
- };
-
- Button databaseBrowseButton = new()
- {
- X = Pos.Right(this.DatabaseField),
- Y = Pos.Bottom(databaseLabel),
- Width = Dim.Fill(),
- Title = browseTitle,
- };
- databaseBrowseButton.Accepting += this.databaseBrowseButton_OnAccepting;
-
- this.importingView = new View
- {
- X = 0,
- Y = Pos.Bottom(this.DatabaseField) + 1,
- Width = Dim.Fill(),
- Height = 1,
- };
-
- this.errorLabel = new Label
- {
- X = 0,
- Y = Pos.Bottom(this.DatabaseField) + 1,
- Width = Dim.Fill(),
- Visible = false,
- Text = "Import failed.",
- };
-
- SpinnerView spinner = new()
- {
- AutoSpin = true,
- Style = new SpinnerStyle.Dots(),
- X = 0,
- };
-
- Label importingLabel = new()
- {
- Title = "Importing...",
- Width = Dim.Auto(),
- X = Pos.Right(spinner) + 1,
- };
-
- this.OkButton.Accepting += this.OkButton_OnAccepting;
- this.OkButton.Enabled = false;
-
- this.Add(tagHierarchyLabel, this.ImportedFileField, importFileBrowseButton, databaseLabel,
- this.DatabaseField, databaseBrowseButton, this.importingView, this.errorLabel);
- this.importingView.Add(spinner, importingLabel);
- this.importingView.Visible = false;
- TagDatabaseService.ImportError += this.TagDatabaseService_OnImportError;
- TagDatabaseService.InitialisationComplete += this.TagDatabaseService_OnInitialisationComplete;
- this.Closing += this.OnClosing;
- }
-
- private void OkButton_OnAccepting(object? sender, CommandEventArgs e)
- {
- if (this.chosenDatabaseLocation is null ||
- string.IsNullOrEmpty(this.ImportedFileField.Text))
- {
- MessageBox.ErrorQuery(
- "Error",
- "One or more files were missing for the import process.\nPlease choose a valid file to import and/or a location for the tag database, and try again.",
- "OK");
- }
- else
- {
- foreach (Button button in this.Buttons) button.Enabled = false;
- this.errorLabel.Visible = false;
- this.importingView.Visible = true;
- TagDatabaseService.StartDatabaseInit(
- this.chosenDatabaseLocation.Value.filePath,
- this.chosenDatabaseLocation.Value.userAllowedOverwrite,
- this.ImportedFileField.Text);
- }
-
- e.Handled = true;
- }
-
- private void OnClosing(object? sender, ToplevelClosingEventArgs e)
- {
- if (this.importingView.Visible)
- {
- e.Cancel = true;
- return;
- }
-
- this.importingView.Visible = false;
- this.errorLabel.Visible = false;
- TagDatabaseService.ImportError -= this.TagDatabaseService_OnImportError;
- TagDatabaseService.InitialisationComplete -= this.TagDatabaseService_OnInitialisationComplete;
-
- this.Closing -= this.OnClosing;
- }
-
- private void OnImportFileBrowseClick(object? sender, CommandEventArgs e)
- {
- string? tagHierarchyFilePath = StandardDialogs.ImportOpenDialog();
- if (tagHierarchyFilePath is not null)
- {
- this.ImportedFileField.Text = tagHierarchyFilePath;
- this.ImportedFileField.CursorPosition = tagHierarchyFilePath.Length;
-
- if (this.chosenDatabaseLocation is not null) this.OkButton.Enabled = true;
- }
-
- e.Handled = true;
- }
-
- private void TagDatabaseService_OnImportError(object? sender, string e)
- {
- Application.Invoke(() =>
- {
- this.importingView.Visible = false;
- this.errorLabel.Visible = true;
- foreach (Button button in this.Buttons) button.Enabled = true;
- MessageBox.ErrorQuery("Error", $"An error occurred trying to import the file:\n{e}", "OK");
- });
- }
-
- private void TagDatabaseService_OnInitialisationComplete(object? sender, EventArgs e)
- {
- Application.Invoke(() =>
- {
- this.importingView.Visible = false;
- this.errorLabel.Visible = false;
- foreach (Button button in this.Buttons) button.Enabled = true;
- this.RequestStop();
- });
- }
-}
\ No newline at end of file
diff --git a/UI/Terminal/Views/Dialogs/UserInputDialog.cs b/UI/Terminal/Views/Dialogs/UserInputDialog.cs
deleted file mode 100644
index 3009fe5..0000000
--- a/UI/Terminal/Views/Dialogs/UserInputDialog.cs
+++ /dev/null
@@ -1,39 +0,0 @@
-using Terminal.Gui.App;
-using Terminal.Gui.Views;
-
-namespace TagHierarchyManager.UI.TerminalUI.Views;
-
-///
-/// A base class implementing a template centered around user-facing input.
-///
-public class UserInputDialog : Dialog
-{
- protected Button OkButton = null!;
- private Button cancelButton = null!;
-
- ///
- /// Initializes a new instance of the class.
- ///
- protected UserInputDialog()
- {
- this.InitialiseUI();
- }
-
- private void InitialiseUI()
- {
- this.OkButton = new Button
- {
- Title = "OK",
- };
-
- this.cancelButton = new Button
- {
- Title = "Cancel",
- };
-
- this.cancelButton.Accepting += (_, _) => Application.RequestStop();
-
- this.AddButton(this.OkButton);
- this.AddButton(this.cancelButton);
- }
-}
\ No newline at end of file
diff --git a/UI/Terminal/Views/EditorWindow/EditorWindow.cs b/UI/Terminal/Views/EditorWindow/EditorWindow.cs
deleted file mode 100644
index 6de7f78..0000000
--- a/UI/Terminal/Views/EditorWindow/EditorWindow.cs
+++ /dev/null
@@ -1,136 +0,0 @@
-using TagHierarchyManager.Models;
-using TagHierarchyManager.UI.TerminalUI.Services;
-using Terminal.Gui.App;
-using Terminal.Gui.Drawing;
-using Terminal.Gui.ViewBase;
-using Terminal.Gui.Views;
-
-namespace TagHierarchyManager.UI.TerminalUI.Views;
-
-///
-/// A comprising of a for browsing the tag hierarchy, and a
-/// for editing tag data.
-///
-// tbh this could be static, but i think i'm done doing this for now lol.
-public class EditorWindow : Window
-{
- internal ImportDialog? ImportDialog = null;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The top menu to base the window position on.
- public EditorWindow(TopMenu topMenu)
- {
- this.BorderStyle = LineStyle.None;
- this.Y = Pos.Bottom(topMenu);
-
- this.TagPane = new TagBrowserPane
- {
- X = 0,
- Y = 0,
- Width = Dim.Percent(20),
- Height = Dim.Fill()! - 1,
- CanFocus = true,
- TabStop = TabBehavior.TabGroup,
- };
- this.EditPane = new TagEditorPane
- {
- X = Pos.Right(this.TagPane),
- Y = 0,
- Width = Dim.Fill(),
- Height = Dim.Fill()! - 1,
- CanFocus = true,
- TabStop = TabBehavior.TabGroup,
- Enabled = false,
- };
- this.StatusBar = new MultiModeStatusBar();
-
- this.Add(this.TagPane, this.EditPane, this.StatusBar);
- TagDatabaseService.InitialisationComplete += this.TagDatabaseService_InitialisationComplete;
- TagDatabaseService.DatabaseInitialising += this.TagDatabaseService_OnDatabaseInitialising;
- TagDatabaseService.ImportError += this.TagDatabaseService_OnImportError;
- TagDatabaseService.InitialisationError += this.TagDatabaseService_OnInitialisationError;
- TagDatabaseService.ExportError += this.TagDatabaseService_OnExportError;
- TagDatabaseService.ExportStarted += this.TagDatabaseService_OnExportStarted;
- TagDatabaseService.ExportCompleted += this.TagDatabaseService_OnExportCompleted;
- }
-
- ///
- /// Gets the inside the window.
- ///
- internal TagEditorPane EditPane { get; }
-
-
- ///
- /// Gets the inside the window.
- ///
- internal TagBrowserPane TagPane { get; }
-
- private MultiModeStatusBar StatusBar { get; }
-
- private void TagDatabaseService_InitialisationComplete(object? sender, EventArgs e)
- {
- if (sender is not TagDatabase) return;
- Application.Invoke(() =>
- {
- this.Enabled = true;
- this.EditPane.Enabled = false;
- });
- }
-
-
- private void TagDatabaseService_OnDatabaseInitialising(object? sender, EventArgs e)
- {
- Application.Invoke(() => { this.Enabled = false; });
- }
-
- private void TagDatabaseService_OnExportCompleted(object? sender, string e)
- {
- Application.Invoke(() =>
- {
- this.Enabled = true;
- this.EditPane.EnableIfTagLoaded();
- });
- }
-
- private void TagDatabaseService_OnExportError(object? sender, string e)
- {
- Application.Invoke(() =>
- {
- MessageBox.ErrorQuery("Error",
- $"An error occurred during the export process.\n\nError message:\n{e}",
- "OK");
- this.Enabled = true;
- this.EditPane.EnableIfTagLoaded();
- });
- }
-
- private void TagDatabaseService_OnExportStarted(object? sender, EventArgs e)
- {
- Application.Invoke(() => { this.Enabled = false; });
- }
-
-
- private void TagDatabaseService_OnImportError(object? sender, string e)
- {
- Application.Invoke(() =>
- {
- this.Enabled = true;
- this.EditPane.Enabled = false;
- });
- }
-
- private void TagDatabaseService_OnInitialisationError(object? sender, string errorMessage)
- {
- Application.Invoke(() =>
- {
- MessageBox.ErrorQuery("Error",
- $"An error has occurred trying to initialise the database.\n\nError message:\n{errorMessage}",
- "OK");
- if (Program.CurrentDatabase == null) return;
- this.Enabled = true;
- this.EditPane.EnableIfTagLoaded();
- });
- }
-}
\ No newline at end of file
diff --git a/UI/Terminal/Views/EditorWindow/MultiModeStatusBar.cs b/UI/Terminal/Views/EditorWindow/MultiModeStatusBar.cs
deleted file mode 100644
index d88bee0..0000000
--- a/UI/Terminal/Views/EditorWindow/MultiModeStatusBar.cs
+++ /dev/null
@@ -1,190 +0,0 @@
-using TagHierarchyManager.Models;
-using TagHierarchyManager.UI.TerminalUI.Services;
-using Terminal.Gui.App;
-using Terminal.Gui.ViewBase;
-using Terminal.Gui.Views;
-
-namespace TagHierarchyManager.UI.TerminalUI.Views;
-
-public class MultiModeStatusBar : View
-{
- private TextOnly textOnlyView = null!;
- private TextWithSpinner textWithSpinnerView = null!;
-
- public MultiModeStatusBar()
- {
- this.InitialiseUI();
- }
-
- private void InitialiseUI()
- {
- this.X = 0;
- this.Y = Pos.AnchorEnd(1);
- this.Width = Dim.Fill();
- this.Height = 1;
- this.Visible = true;
- this.CanFocus = false;
-
- this.textWithSpinnerView = new TextWithSpinner
- {
- Visible = false,
- };
-
- this.textOnlyView = new TextOnly
- {
- Visible = true,
- };
- // Set the label text directly
- this.textOnlyView.TextLabel.Text = "Ready.";
-
- this.Add(this.textWithSpinnerView, this.textOnlyView);
- TagDatabaseService.InitialisationError += this.TagDatabaseService_OnInitialisationError;
- TagDatabaseService.InitialisationComplete += this.TagDatabaseService_InitialisationComplete;
- TagDatabaseService.ImportError += this.TagDatabaseService_OnImportError;
- TagDatabaseService.DatabaseInitialising += this.TagDatabaseService_OnDatabaseInitialising;
- TagDatabaseService.TagSaved += this.TagDatabaseService_OnTagSaved;
- TagDatabaseService.TagDeleted += this.TagDatabaseService_OnTagDeleted;
- TagDatabaseService.ExportStarted += this.TagDatabaseService_OnExportStarted;
- TagDatabaseService.ExportCompleted += this.TagDatabaseService_OnExportCompleted;
- TagDatabaseService.ExportError += this.TagDatabaseService_OnExportError;
- TagDatabaseService.SearchFinished += this.TagDatabaseService_OnSearchFinished;
- }
-
- private void TagDatabaseService_InitialisationComplete(object? sender, EventArgs e)
- {
- this.UpdateStatusBarTextOnly("Database initialized successfully.");
- }
-
- private void TagDatabaseService_OnDatabaseInitialising(object? sender, EventArgs e)
- {
- this.UpdateStatusBarWithSpinner("Initializing database...");
- }
-
- private void TagDatabaseService_OnExportCompleted(object? sender, string filePath)
- {
- this.UpdateStatusBarTextOnly($"Database exported successfully to \"{filePath}\".");
- }
-
- private void TagDatabaseService_OnExportError(object? sender, string e)
- {
- this.UpdateStatusBarTextOnly("Export failed.");
- }
-
- private void TagDatabaseService_OnExportStarted(object? sender, EventArgs e)
- {
- this.UpdateStatusBarWithSpinner("Exporting database...");
- }
-
- private void TagDatabaseService_OnImportError(object? sender, string e)
- {
- this.UpdateStatusBarTextOnly("Error importing: " + e);
- }
-
- private void TagDatabaseService_OnSearchFinished(object? sender, List result)
- {
- if (result.Count == 0) this.Clear();
- else this.UpdateStatusBarTextOnly(result.Count == 1 ? "One result found." : $"{result.Count} results found.");
- }
-
- private void TagDatabaseService_OnInitialisationError(object? sender, string e)
- {
- this.UpdateStatusBarTextOnly("Error initializing database: " + e);
- }
-
- private void TagDatabaseService_OnTagDeleted(object? sender, (int tagId, string tagName) tagData)
- {
- this.UpdateStatusBarTextOnly($"Tag \"{tagData.tagName}\" deleted successfully.");
- }
-
- private void TagDatabaseService_OnTagSaved(object? sender, Tag tag)
- {
- this.UpdateStatusBarTextOnly($"Tag \"{tag.Name}\" saved successfully.");
- }
-
- private void Clear()
- {
- Application.Invoke(() =>
- {
- this.textWithSpinnerView.Visible = false;
- this.textOnlyView.Visible = false;
- });
- }
-
- private void UpdateStatusBarTextOnly(string message)
- {
- Application.Invoke(() =>
- {
- this.textOnlyView.TextLabel.Text = message;
- this.textWithSpinnerView.Visible = false;
- this.textOnlyView.Visible = true;
- });
- }
-
- private void UpdateStatusBarWithSpinner(string message)
- {
- Application.Invoke(() =>
- {
- this.textWithSpinnerView.TextLabel.Text = message;
- this.textWithSpinnerView.Visible = true;
- this.textOnlyView.Visible = false;
- });
- }
-
- private sealed class TextOnly : View
- {
- internal readonly Label TextLabel;
-
- public TextOnly()
- {
- this.X = 0;
- this.Y = 0;
- this.Width = Dim.Fill();
- this.Height = 1;
-
- this.TextLabel = new Label
- {
- X = 0,
- Y = 0,
- Width = Dim.Fill(),
- Height = 1,
- Text = string.Empty,
- };
-
- this.Add(this.TextLabel);
- }
- }
-
- private sealed class TextWithSpinner : View
- {
- internal readonly Label TextLabel;
-
- public TextWithSpinner()
- {
- this.X = 0;
- this.Y = 0;
- this.Width = Dim.Fill();
- this.Height = 1;
-
- SpinnerView spinner = new()
- {
- X = 0,
- Y = 0,
- Width = 1,
- Height = 1,
- AutoSpin = true,
- Style = new SpinnerStyle.Dots(),
- };
-
- this.TextLabel = new Label
- {
- X = Pos.Right(spinner) + 1,
- Y = 0,
- Width = Dim.Fill(),
- Height = 1,
- Text = string.Empty,
- };
-
- this.Add(spinner, this.TextLabel);
- }
- }
-}
\ No newline at end of file
diff --git a/UI/Terminal/Views/EditorWindow/TagBrowserPane.cs b/UI/Terminal/Views/EditorWindow/TagBrowserPane.cs
deleted file mode 100644
index 5f5e87e..0000000
--- a/UI/Terminal/Views/EditorWindow/TagBrowserPane.cs
+++ /dev/null
@@ -1,240 +0,0 @@
-using System.Collections.ObjectModel;
-using TagHierarchyManager.Common;
-using TagHierarchyManager.Models;
-using TagHierarchyManager.UI.TerminalUI.Services;
-using Terminal.Gui.Input;
-using Terminal.Gui.ViewBase;
-using Terminal.Gui.Views;
-
-namespace TagHierarchyManager.UI.TerminalUI.Views;
-
-///
-/// A paned view for browsing the 's tag hierarchy tree.
-///
-public class TagBrowserPane : View
-{
- internal readonly Button NewTagButton = new();
- private ComboBox modeSelector = null!;
- private TextField queryField = null!;
- private ListView resultsList = null!;
- private TreeView tagTree = null!;
- private CheckBox searchAliasesCheckbox = null!;
- private bool resultsListAutoSelect = true;
-
- ///
- /// Initializes a new instance of the class.
- ///
- public TagBrowserPane()
- {
- this.InitialiseUI();
- }
-
- private void TagDatabaseService_OnSearchFinished(object? sender, List results)
- {
- this.resultsListAutoSelect = true;
- if (results.Count > 0)
- this.resultsList.SetSource(new ObservableCollection(results));
- else
- this.resultsList.SetSource(["No results found."]);
- this.resultsList.SelectedItem = 0;
- this.resultsListAutoSelect = false;
- }
-
- private void InitialiseUI()
- {
- TabView tabView = new()
- {
- X = 0,
- Y = 0,
- Width = Dim.Fill(),
- Height = Dim.Fill(),
- CanFocus = true,
- };
-
- Tab tagTreeTab = new()
- {
- DisplayText = "Hierarchy Tree",
- View = new View
- {
- Width = Dim.Fill(),
- Height = Dim.Fill(),
- CanFocus = true,
- },
- };
-
- this.tagTree = new TreeView
- {
- X = 0,
- Y = 0,
- Width = Dim.Fill(),
- Height = Dim.Fill() ! - 2,
- CanFocus = true,
- MultiSelect = false,
- };
-
- this.NewTagButton.Y = Pos.Bottom(this.tagTree);
- this.NewTagButton.Title = "New tag...";
- this.NewTagButton.Width = Dim.Fill();
- this.NewTagButton.CanFocus = true;
-
- tagTreeTab.View.Add(this.tagTree, this.NewTagButton);
-
- Tab searchTab = new()
- {
- DisplayText = "Search",
- View = new View
- {
- Width = Dim.Fill(),
- Height = Dim.Fill(),
- CanFocus = true,
- },
- };
-
- this.queryField = new TextField
- {
- Width = Dim.Percent(75),
- CanFocus = true,
- Height = 1,
- };
- this.modeSelector = new ComboBox
- {
- Y = Pos.Bottom(this.queryField) + 2,
- Width = Dim.Fill(),
- Height = 5,
- CanFocus = true,
- ReadOnly = true,
- };
- this.searchAliasesCheckbox = new CheckBox
- {
- Y = Pos.Top(this.modeSelector) - 1,
- Text = "Search in aliases",
- CanFocus = true,
- };
- Line separatorLine = new()
- {
- Y = Pos.Bottom(this.modeSelector),
- };
- Button searchButton = new()
- {
- X = Pos.Right(this.queryField),
- Y = 0,
- Text = "Search",
- Width = Dim.Fill(),
- CanFocus = true,
- };
- this.modeSelector.SetSource(["Fuzzy search", "Starts with", "Ends with", "Exact match"]);
- this.modeSelector.SelectedItem = 0;
- this.resultsList = new ListView
- {
- X = 0,
- Y = Pos.Bottom(this.modeSelector) + 1,
- Width = Dim.Fill(),
- Height = Dim.Fill(),
- CanFocus = true,
- };
-
- searchTab.View.Add(this.queryField, searchButton, this.searchAliasesCheckbox, this.modeSelector,
- separatorLine, this.resultsList);
- tabView.AddTab(tagTreeTab, true);
- tabView.AddTab(searchTab, false);
- this.Add(tabView);
- this.Enabled = false;
-
- TagDatabaseService.InitialisationComplete += this.TagDatabaseService_OnInitialised;
- TagDatabaseService.TagAdded += this.TagDatabaseService_OnTagAdded;
- TagDatabaseService.TagDeleted += this.TagDatabaseService_OnTagDeleted;
- TagDatabaseService.TagSaved += this.TagDatabaseService_OnTagSaved;
- TagDatabaseService.SearchFinished += this.TagDatabaseService_OnSearchFinished;
- searchButton.Accepting += (_, args) =>
- {
- if (string.IsNullOrWhiteSpace(this.queryField.Text)) return;
- bool isChecked = this.searchAliasesCheckbox.CheckedState == CheckState.Checked;
- TagDatabaseService.SearchTags(this.queryField.Text,
- (TagDatabaseSearchMode)this.modeSelector.SelectedItem, isChecked);
- args.Handled = true;
- };
- this.queryField.KeyDown += (_, args) =>
- {
- if (args.KeyCode != Key.Enter || string.IsNullOrWhiteSpace(this.queryField.Text)) return;
- bool isChecked = this.searchAliasesCheckbox.CheckedState == CheckState.Checked;
- TagDatabaseService.SearchTags(this.queryField.Text,
- (TagDatabaseSearchMode)this.modeSelector.SelectedItem, false);
- args.Handled = true;
- };
-
- this.tagTree.ObjectActivated += (_, args) =>
- {
- if (args.ActivatedObject is TagHierarchyTreeNode node)
- Program.MainView.EditPane.LoadTag(node.AssociatedTag);
- };
-
- this.resultsList.OpenSelectedItem += (_, args) =>
- {
- if (args.Value is not Tag selectedTag || this.resultsListAutoSelect) return;
- Program.MainView.EditPane.LoadTag(selectedTag);
- };
- }
-
- private void RefreshTagTree(TagDatabase db)
- {
- this.tagTree.ClearObjects();
- IEnumerable topLevelNodes =
- db.Tags
- .Where(tag => tag.IsTopLevel)
- .OrderBy(tag => tag.Name, StringComparer.CurrentCultureIgnoreCase)
- .Select(tag => new TagHierarchyTreeNode(tag));
- this.tagTree.AddObjects(topLevelNodes);
- }
-
- private void TagDatabaseService_OnInitialised(object? sender, EventArgs e)
- {
- if (sender is not TagDatabase db) return;
- this.RefreshTagTree(db);
- }
-
- private void TagDatabaseService_OnTagAdded(object? sender, Tag tag)
- {
- foreach (int parent in tag.ParentIds)
- this.tagTree.Objects.Cast().Where(node => node.AssociatedTag.Id == parent)
- .ToList()
- .ForEach(node => this.tagTree.RefreshObject(node));
- }
-
- private void TagDatabaseService_OnTagDeleted(object? sender, (int Id, string Name) tag)
- {
- this.tagTree.Objects.Cast()
- .Where(node => node.AssociatedTag.Id == tag.Id).ToList()
- .ForEach(node => this.tagTree.Remove(node));
- this.resultsList.SetSource(new ObservableCollection