diff --git a/Toolkit Launcher/Form/FrmMain.cs b/Toolkit Launcher/Form/FrmMain.cs index 8376fe5..be33fdf 100644 --- a/Toolkit Launcher/Form/FrmMain.cs +++ b/Toolkit Launcher/Form/FrmMain.cs @@ -1,15 +1,21 @@ +using Toolkit_Launcher.Models; +using Toolkit_Launcher.Services; namespace Toolkit_Launcher; public partial class FrmMain : Form + private readonly SettingsService _settingsService; + private readonly ImageCacheService _imageCacheService; { public bool Leaver = true; private static string[] _args = { }; private static MyEnums.TargetType _targetType; - public FrmMain(string[] args) + public FrmMain(string[] args, SettingsService settingsService, ImageCacheService imageCacheService) { if (args == null) throw new ArgumentNullException(nameof(args)); InitializeComponent(); + _settingsService = settingsService ?? throw new ArgumentNullException(nameof(settingsService)); + _imageCacheService = imageCacheService ?? throw new ArgumentNullException(nameof(imageCacheService)); #if DEBUG args = new[] { @@ -27,58 +33,22 @@ public FrmMain(string[] args) private void FrmMain_Load(object sender, EventArgs e) { - PublicClass.ImageList = imageList1; - InitializeList(); + // PublicClass.ImageList = imageList1; // Removed: ImageCacheService now handles images + // InitializeList(); // Removed: SettingsService handles loading AddItems(); AddSetting(); AddOption(); contextMenuStrip1.Show(Cursor.Position); } - private static void InitializeList() - { - if (!File.Exists(PublicClass.PATH)) return; - LoadFile(PublicClass.PATH); - } - - private static void LoadFile(string fileName) - { - try - { - PublicClass.ListInfo = LoadJson(fileName); - } - catch (Exception ex) - { - MyMessage.ShowError(ex.Message); - } - finally - { - PublicClass.TempListInfo = PublicClass.ListInfo; - } - } - - private static T LoadJson(string fileName) - { - if (fileName.IsEmpty()) return default!; - - var objectOut = default(T); - - try - { - objectOut = JsonConvert.DeserializeObject(File.ReadAllText(fileName)); - } - catch (Exception ex) - { - MyMessage.ShowError(ex.Message); - } - - return objectOut!; - } + // Removed InitializeList, LoadFile, LoadJson as SettingsService handles this private void AddItems() { - if (PublicClass.ListInfo == null!) return; - AddAll(contextMenuStrip1, PublicClass.ListInfo); + if (_settingsService.CurrentSettings == null!) return; // Use SettingsService + contextMenuStrip1.SuspendLayout(); + AddAll(contextMenuStrip1, _settingsService.CurrentSettings); // Use SettingsService + contextMenuStrip1.ResumeLayout(false); } private void AddSetting() @@ -92,7 +62,7 @@ private void AddSetting() { Leaver = false; contextMenuStrip1.Hide(); - var frmSetting = new FrmSetting(); + var frmSetting = new FrmSetting(_settingsService, _imageCacheService); frmSetting.ShowDialog(); frmSetting.Dispose(); Application.Exit(); @@ -146,13 +116,16 @@ private void AddAll(ToolStrip menu, ListInfo listInfo) default: { if (string.IsNullOrWhiteSpace(group.Value.FilePath)) continue; - var image = group.Value.IconPath.GetImage(); - if (image == null!) + var image = _imageCacheService.GetImage(group.Value.IconPath); // Use ImageCacheService + if (image == null || image == _imageCacheService.GetImage(MyEnums.GroupType.Null)) // Check against default null image { - image = group.Type.GetImage(); - menu.Items.Add(group.Type.ToString(), image); - return; + image = _imageCacheService.GetImage(group.Type); // Use ImageCacheService + // menu.Items.Add(group.Type.ToString(), image); // This line seems to be an early exit, might need review + // return; // This line seems to be an early exit, might need review } + // Ensure image is not null before creating ToolStripMenuItem, or handle it + if (image == null) image = _imageCacheService.GetImage(MyEnums.GroupType.Null); + var result = new ToolStripMenuItem(group.Identify.Name, image); result.Click += (_, _) => @@ -197,11 +170,13 @@ private List AddAll(GroupInfo groupInfo) default: { if (string.IsNullOrWhiteSpace(group.Value.FilePath)) continue; - var image = group.Value.IconPath.GetImage(); - if (image == null!) + var image = _imageCacheService.GetImage(group.Value.IconPath); // Use ImageCacheService + if (image == null || image == _imageCacheService.GetImage(MyEnums.GroupType.Null)) // Check against default null image { - image = group.Type.GetImage(); + image = _imageCacheService.GetImage(group.Type); // Use ImageCacheService } + // Ensure image is not null before creating ToolStripMenuItem, or handle it + if (image == null) image = _imageCacheService.GetImage(MyEnums.GroupType.Null); var item = new ToolStripMenuItem(group.Identify.Name, image); item.Click += (_, _) => @@ -226,4 +201,4 @@ private void FrmMain_Deactivate(object sender, EventArgs e) if (!Leaver) return; Application.Exit(); } -} \ No newline at end of file +} diff --git a/Toolkit Launcher/Form/FrmSetting.cs b/Toolkit Launcher/Form/FrmSetting.cs index 8a74e26..f326509 100644 --- a/Toolkit Launcher/Form/FrmSetting.cs +++ b/Toolkit Launcher/Form/FrmSetting.cs @@ -1,183 +1,196 @@ -namespace Toolkit_Launcher; +using Toolkit_Launcher.UI.Helpers; +using Toolkit_Launcher.Models; +namespace Toolkit_Launcher; // Keep namespace for Form +using Toolkit_Launcher.Services; +using Toolkit_Launcher.ItemManager; // The class, not the old Helper namespace +using Toolkit_Launcher.Utilities; // For TreeViewHelper and MyMessage +using System; +using System.Collections.Generic; +using System.IO; // For Path +using System.Linq; // For Linq extensions +using System.Windows.Forms; + public partial class FrmSetting : Form { #region Variable + private bool _editor; // To prevent event-driven updates during programmatic changes + private readonly SettingsService _settingsService; + private readonly ImageCacheService _imageCacheService; + private readonly ItemsManager _itemsManager; // Instance of ItemsManager + #endregion - private static bool _editor; - - #endregion Variable - - public FrmSetting() + public FrmSetting(SettingsService settingsService, ImageCacheService imageCacheService) { InitializeComponent(); - imageList1 = PublicClass.ImageList; + _settingsService = settingsService ?? throw new ArgumentNullException(nameof(settingsService)); + _imageCacheService = imageCacheService ?? throw new ArgumentNullException(nameof(imageCacheService)); + + _settingsService.ReloadTempSettings(); + _itemsManager = new ItemsManager(_settingsService.TempSettings); + + _itemsManager.ItemsChanged += ItemsManager_ItemsChanged; + + // Setup ImageList for the TreeView + // The ImageList from the designer (imageList1) can be used if its properties (ColorDepth, ImageSize) are suitable. + // Or, create a new one if more control is needed. Let's use the designer's imageList1. + // Ensure it's cleared if it persists images from designer. + imageList1.Images.Clear(); treeOption.ImageList = imageList1; } - #region Load + private void ItemsManager_ItemsChanged(object sender, EventArgs e) + { + string previouslySelectedPath = treeOption.SelectedNode?.Tag as string; + TreeViewHelper.LoadAll(treeOption, _settingsService.TempSettings, _imageCacheService); + if (previouslySelectedPath != null) + { + TreeViewHelper.SelectNodeByPath(treeOption, previouslySelectedPath); + } + } + #region Load private void FrmSetting_Load(object sender, EventArgs e) { - if (PublicClass.TempListInfo == null!) return; - Utility.LoadAll(treeOption, PublicClass.TempListInfo); + if (_settingsService.TempSettings == null) + { + // Consider initializing TempSettings with a default ListInfo if null + _settingsService.TempSettings = new ListInfo(); // Ensure it's not null + _itemsManager = new ItemsManager(_settingsService.TempSettings); // Re-assign if TempSettings was null + _itemsManager.ItemsChanged += ItemsManager_ItemsChanged; // Re-subscribe + } + TreeViewHelper.LoadAll(treeOption, _settingsService.TempSettings, _imageCacheService); } - - #endregion Load + #endregion #region Save - private void btnSaveAll_Click(object sender, EventArgs e) { SaveFile(); } - private static void SaveFile() + private void SaveFile() { - if (PublicClass.TempListInfo.IsEmpty()) + if (_settingsService.TempSettings.ListGroup == null || !_settingsService.TempSettings.ListGroup.Any()) { - MyMessage.ShowWarning("List is Empty, Please Check it again!!!"); - return; + // MyMessage.ShowWarning("List is Empty, Please Check it again!!!"); + // return; + // Allow saving an empty list if user cleared everything. } - - if (!MyMessage.OkCancel("Save Settings.\n\n" + - "Click \"OK\" to confirm.\n\n" + - "Click \"Cancel\" to cancel.")) return; - var save = SaveJson(PublicClass.TempListInfo, PublicClass.PATH); - if (!save.Item1) - MyMessage.ShowError(save.message); - Application.Exit(); - } - - private static (bool, string message) SaveJson(T serializableObject, string fileName) - { - if (serializableObject == null) return (false, "incorrect settings.json"); - - try + if (!MyMessage.OkCancel("Save Settings.\n\nClick \"OK\" to confirm.\n\nClick \"Cancel\" to cancel.")) { - File.WriteAllText(fileName, JsonConvert.SerializeObject(serializableObject, Formatting.Indented)); - return (true, ""); - } - catch (Exception ex) - { - return (false, ex.Message); + return; } + _settingsService.SaveSettings(); + Application.Exit(); } - - #endregion Save + #endregion #region Add Items + private void HandleAddItem(MyEnums.GroupType type) + { + string parentPath = null; + if (treeOption.SelectedNode != null) { + var selectedGroupType = _itemsManager.GetGroupType(treeOption.SelectedNode.Tag as string); + if (selectedGroupType == MyEnums.GroupType.Header) { + parentPath = treeOption.SelectedNode.Tag as string; + } else { + // If selected is not a header, add to its parent, or root if no parent + var parentNode = treeOption.SelectedNode.Parent; + if (parentNode != null) { + parentPath = parentNode.Tag as string; + } + } + } - private void btnAddHeader_Click(object sender, EventArgs e) - { - ItemsManager.AddGroup(MyEnums.GroupType.Header, treeOption); - } - - private void btnAddSeparator_Click(object sender, EventArgs e) - { - ItemsManager.AddGroup(MyEnums.GroupType.Separator, treeOption); - } - - private void btnAddItem_Click(object sender, EventArgs e) - { - ItemsManager.AddGroup(MyEnums.GroupType.Item, treeOption); + GroupInfo newGroup = _itemsManager.AddGroup(type, parentPath); + // TreeViewHelper.LoadAll is called by ItemsManager_ItemsChanged event. + // Select the new node: + TreeViewHelper.SelectNodeByPath(treeOption, newGroup.Identify.FullPath); + treeOption.Focus(); + // If it's not a separator, allow immediate rename + if (type != MyEnums.GroupType.Separator) { + treeOption.SelectedNode?.BeginEdit(); + } } - #endregion Add Items + private void btnAddHeader_Click(object sender, EventArgs e) => HandleAddItem(MyEnums.GroupType.Header); + private void btnAddSeparator_Click(object sender, EventArgs e) => HandleAddItem(MyEnums.GroupType.Separator); + private void btnAddItem_Click(object sender, EventArgs e) => HandleAddItem(MyEnums.GroupType.Item); + #endregion #region TreeView Selected - private void treeOption_AfterSelect(object sender, TreeViewEventArgs e) { - if (SelectedNull()) return; - var selected = treeOption.SelectedNode; - var type = ItemsManager.GetType(selected.FullPath); - GetDetail(type); + if (SelectedNull() || e.Node == null || e.Node.Tag == null) { + EnableType(MyEnums.GroupType.Null); // Treat as no valid selection + ClearDetail(); + return; + } + var selectedPath = e.Node.Tag as string; + var type = _itemsManager.GetGroupType(selectedPath); + GetDetail(selectedPath, type); EnableType(type); } private void EnableType(MyEnums.GroupType type) { - switch (type) - { - case MyEnums.GroupType.Item: - gbDetail.Enabled = true; - panelAdd.Enabled = false; - addToolStripMenuItem.Enabled = false; - renameToolStripMenuItem.Enabled = true; - break; - - case MyEnums.GroupType.Separator: - gbDetail.Enabled = false; - panelAdd.Enabled = false; - addToolStripMenuItem.Enabled = false; - renameToolStripMenuItem.Enabled = false; - break; - - case MyEnums.GroupType.Header: - gbDetail.Enabled = true; - panelAdd.Enabled = true; - addToolStripMenuItem.Enabled = true; - renameToolStripMenuItem.Enabled = true; - break; - - default: - gbDetail.Enabled = false; - panelAdd.Enabled = true; - addToolStripMenuItem.Enabled = true; - renameToolStripMenuItem.Enabled = false; - break; - } - } + gbDetail.Enabled = type == MyEnums.GroupType.Item || type == MyEnums.GroupType.Header; + panelAdd.Enabled = true; // Always allow adding (to root or as child of header) + addToolStripMenuItem.Enabled = true; - #endregion TreeView Selected + renameToolStripMenuItem.Enabled = type == MyEnums.GroupType.Item || type == MyEnums.GroupType.Header; + btnPath.Enabled = type == MyEnums.GroupType.Item; - #region Get Detail + // Context menu add items should be enabled based on what can be added + // e.g. cannot add a header inside an item. + bool canBeParent = type == MyEnums.GroupType.Header || type == MyEnums.GroupType.Null; + headerToolStripMenuItem.Enabled = canBeParent; + itemToolStripMenuItem.Enabled = canBeParent; + separatorToolStripMenuItem.Enabled = canBeParent; + } + #endregion - private void GetDetail(MyEnums.GroupType type) + #region Get Detail + private void GetDetail(string itemPath, MyEnums.GroupType type) { - switch (type) + _editor = true; // Prevent SaveTemp during population + if (type == MyEnums.GroupType.Separator || type == MyEnums.GroupType.Null) { - case MyEnums.GroupType.Separator: - ClearDetail(); - break; + ClearDetail(); + _editor = false; + return; + } - default: - GroupDetail(); - break; + var detail = _itemsManager.GetGroupInfoByPath(itemPath); + if (detail == null) + { + ClearDetail(); + _editor = false; + return; } - } - private void GroupDetail() - { - var detail = ItemsManager.GetGroupDetail(treeOption); txtName.Text = detail.Identify.Name; txtPath.Text = detail.Value.FilePath; txtIcon.Text = detail.Value.IconPath; txtCommand.Text = detail.Advance.Commands; chkAdmin.Checked = detail.Advance.Admin; chkCMD.Checked = detail.Advance.CMD; - if (detail.Advance.CmdType == MyEnums.CMDType.C) - { - radC.Checked = true; - } - else - { - radK.Checked = true; - } + radC.Checked = detail.Advance.CmdType == MyEnums.CMDType.C; + radK.Checked = detail.Advance.CmdType == MyEnums.CMDType.K; - _editor = true; listTarget.ClearSelected(); - for (var i = 0; i < detail.Advance.TargetTypes.Count; i++) + if (detail.Advance.TargetTypes != null) { - if (detail.Advance.TargetTypes[i]) + for (var i = 0; i < detail.Advance.TargetTypes.Count && i < listTarget.Items.Count; i++) { - listTarget.SelectedIndex = i; + if (detail.Advance.TargetTypes[i]) + { + listTarget.SetSelected(i, true); + } } } - _editor = false; - btnPath.Enabled = detail.Type != MyEnums.GroupType.Header; } private void ClearDetail() @@ -190,310 +203,300 @@ private void ClearDetail() chkCMD.Checked = false; radK.Checked = false; radC.Checked = true; - _editor = true; listTarget.ClearSelected(); - _editor = false; } + #endregion - #endregion Get Detail - - #region Menu - + #region Menu & MouseDown private void treeOption_MouseDown(object sender, MouseEventArgs e) { - var hit = treeOption.HitTest(e.X, e.Y); - if (hit.Node == null) + var hitNode = treeOption.GetNodeAt(e.X, e.Y); + if (e.Button == MouseButtons.Right) // Only change selection on right click if a node is hit + { + if (hitNode != null) treeOption.SelectedNode = hitNode; + } else { // For left click, always update selection + treeOption.SelectedNode = hitNode; + } + + + if (hitNode == null) { - treeOption.SelectedNode = null; - MenuController(false, e); - gbDetail.Enabled = true; - panelAdd.Enabled = true; - ClearDetail(); EnableType(MyEnums.GroupType.Null); + ClearDetail(); + if (e.Button == MouseButtons.Right) MenuController(false, e); return; } - treeOption.SelectedNode = hit.Node; - MenuController(true, e); + if (e.Button == MouseButtons.Right) MenuController(true, e); } - private void MenuController(bool detected, MouseEventArgs e) + private void MenuController(bool nodeSelected, MouseEventArgs e) { - if (e.Button == MouseButtons.Left) return; - removeToolStripMenuItem.Enabled = detected; - moveUpToolStripMenuItem.Enabled = detected; - moveDownToolStripMenuItem.Enabled = detected; - renameToolStripMenuItem.Enabled = detected; - if (SelectedNull()) return; - int itemCount; - if (treeOption.SelectedNode.Parent == null) - { - itemCount = treeOption.Nodes.Count - 1; - } - else - { - itemCount = treeOption.SelectedNode.Nodes.Count - 1; - } - - if (treeOption.SelectedNode.Index == itemCount) - { - moveDownToolStripMenuItem.Enabled = false; + // This method is for context menu population based on selection. + // EnableType already handles some of this for the main panel. + MyEnums.GroupType type = MyEnums.GroupType.Null; + if (nodeSelected && treeOption.SelectedNode != null && treeOption.SelectedNode.Tag != null) { + type = _itemsManager.GetGroupType(treeOption.SelectedNode.Tag as string); } - if (treeOption.SelectedNode.Index == 0) - { - moveUpToolStripMenuItem.Enabled = false; + removeToolStripMenuItem.Enabled = nodeSelected && type != MyEnums.GroupType.Null; + renameToolStripMenuItem.Enabled = nodeSelected && (type == MyEnums.GroupType.Item || type == MyEnums.GroupType.Header); + + bool canBeParent = type == MyEnums.GroupType.Header || type == MyEnums.GroupType.Null; // Null means root + addToolStripMenuItem.Enabled = canBeParent; // This is the main "Add" menu item + headerToolStripMenuItem.Enabled = canBeParent; + itemToolStripMenuItem.Enabled = canBeParent; + separatorToolStripMenuItem.Enabled = canBeParent; + + // Move Up/Down logic + moveUpToolStripMenuItem.Enabled = false; + moveDownToolStripMenuItem.Enabled = false; + if (nodeSelected && type != MyEnums.GroupType.Null) { + var selectedNode = treeOption.SelectedNode; + var parentCollection = selectedNode.Parent?.Nodes ?? treeOption.Nodes; + int currentIndex = parentCollection.IndexOf(selectedNode); + if (currentIndex > 0) moveUpToolStripMenuItem.Enabled = true; + if (currentIndex < parentCollection.Count - 1) moveDownToolStripMenuItem.Enabled = true; } } + #endregion - #endregion Menu - - #region Menu Button - + #region Menu Button Actions private void treeOption_KeyDown(object sender, KeyEventArgs e) { + if (SelectedNull() || treeOption.SelectedNode.Tag == null) return; + var selectedPath = treeOption.SelectedNode.Tag as string; + if (e.KeyCode == Keys.Delete) { - ItemsManager.Remove(treeOption); + if (MyMessage.OkCancel($"Press \"OK\" to confirm the deletion \"{treeOption.SelectedNode.Text}\"\nPress \"Cancel\" to confirm cancellation")) + { + _itemsManager.RemoveGroup(selectedPath); + } } - if (SelectedNull()) return; - if (e.KeyCode == Keys.F2) + else if (e.KeyCode == Keys.F2 && renameToolStripMenuItem.Enabled) { treeOption.LabelEdit = true; - if (!treeOption.SelectedNode.IsEditing) - { - treeOption.SelectedNode.BeginEdit(); - } + treeOption.SelectedNode.BeginEdit(); } } private void treeOption_AfterLabelEdit(object sender, NodeLabelEditEventArgs e) { - if (e.Label == null) return; - if (e.Label.Length > 0) + treeOption.LabelEdit = false; // Ensure LabelEdit is reset + if (string.IsNullOrWhiteSpace(e.Label)) // Edit cancelled or empty { - if (ItemsManager.Rename(treeOption, e.Label)) - { - e.Node.EndEdit(false); - treeOption.LabelEdit = false; - txtName.Text = e.Label; - } - else - { - e.CancelEdit = true; - e.Node.BeginEdit(); - } + e.CancelEdit = true; + return; } - else + + var originalPath = e.Node.Tag as string; + if (string.IsNullOrEmpty(originalPath)) { e.CancelEdit = true; - MyMessage.ShowError("Invalid name.\nThe label cannot be blank"); - e.Node.BeginEdit(); + return; + } + + // Check if new name is same as old name (derived from path) + var oldName = originalPath.Contains("\") ? originalPath.Substring(originalPath.LastIndexOf("\") + 1) : originalPath; + if (e.Label == oldName) { + e.CancelEdit = true; // No change + return; + } + + + if (!_itemsManager.RenameGroup(originalPath, e.Label)) + { + e.CancelEdit = true; + MyMessage.ShowError("Failed to rename. Name might be a duplicate or invalid."); + } + else + { + // Success. ItemsManager_ItemsChanged will refresh. + // After refresh, try to reselect the item with its new path. + var parentPath = originalPath.Contains("\") ? originalPath.Substring(0, originalPath.LastIndexOf("\")) : ""; + var newPath = string.IsNullOrEmpty(parentPath) ? e.Label : $"{parentPath}\{e.Label}"; + // The event handler will call LoadAll, then try to reselect using the old path. + // We need to ensure the selection is updated to the new path. + // So, FrmSetting.ItemsManager_ItemsChanged needs to be smarter about selection after rename. + // For now, the event handler reloads and tries to select original path, which won't exist. + // A quick fix: update tag here, so that re-selection logic might find it IF name is path component. + // e.Node.Tag = newPath; // This is tricky because tree is about to be rebuilt. + // The ItemsChanged event handler should ideally handle selection restoration. } } private void removeToolStripMenuItem_Click(object sender, EventArgs e) { - ItemsManager.Remove(treeOption); + if (SelectedNull() || treeOption.SelectedNode.Tag == null) return; + var selectedPath = treeOption.SelectedNode.Tag as string; + if (MyMessage.OkCancel($"Press \"OK\" to confirm the deletion \"{treeOption.SelectedNode.Text}\"\nPress \"Cancel\" to confirm cancellation")) + { + _itemsManager.RemoveGroup(selectedPath); + } } private void moveUpToolStripMenuItem_Click(object sender, EventArgs e) { - ItemsManager.MoveUpDown(MyEnums.MoveType.MoveUp, treeOption); + if (SelectedNull() || treeOption.SelectedNode.Tag == null) return; + var selectedPath = treeOption.SelectedNode.Tag as string; + _itemsManager.MoveGroup(selectedPath, MyEnums.MoveType.MoveUp); } private void moveDownToolStripMenuItem_Click(object sender, EventArgs e) { - ItemsManager.MoveUpDown(MyEnums.MoveType.MoveDown, treeOption); + if (SelectedNull() || treeOption.SelectedNode.Tag == null) return; + var selectedPath = treeOption.SelectedNode.Tag as string; + _itemsManager.MoveGroup(selectedPath, MyEnums.MoveType.MoveDown); } - private void headerToolStripMenuItem_Click(object sender, EventArgs e) + private void ContextMenuAdd_Click(object sender, EventArgs e) { - ItemsManager.AddGroup(MyEnums.GroupType.Header, treeOption); - } + MyEnums.GroupType typeToAdd = MyEnums.GroupType.Item; + if (sender == headerToolStripMenuItem) typeToAdd = MyEnums.GroupType.Header; + else if (sender == separatorToolStripMenuItem) typeToAdd = MyEnums.GroupType.Separator; - private void separatorToolStripMenuItem_Click(object sender, EventArgs e) - { - ItemsManager.AddGroup(MyEnums.GroupType.Separator, treeOption); + string parentPath = null; + if(!SelectedNull() && treeOption.SelectedNode.Tag != null && _itemsManager.GetGroupType(treeOption.SelectedNode.Tag as string) == MyEnums.GroupType.Header) + { + parentPath = treeOption.SelectedNode.Tag as string; + } + HandleAddItem(typeToAdd); // Let HandleAddItem figure out parent based on current selection if needed } - private void itemToolStripMenuItem_Click(object sender, EventArgs e) - { - ItemsManager.AddGroup(MyEnums.GroupType.Item, treeOption); - } private void renameToolStripMenuItem_Click(object sender, EventArgs e) { - if (SelectedNull()) return; + if (SelectedNull() || !renameToolStripMenuItem.Enabled) return; treeOption.LabelEdit = true; - if (!treeOption.SelectedNode.IsEditing) - { - treeOption.SelectedNode.BeginEdit(); - } + treeOption.SelectedNode.BeginEdit(); } + #endregion - #endregion Menu Button - - #region Button Events - + #region Button Events (Detail Panel) private void btnName_Click(object sender, EventArgs e) { - if (ItemsManager.Rename(treeOption, txtName.Text)) + if (SelectedNull() || treeOption.SelectedNode.Tag == null) return; + var selectedPath = treeOption.SelectedNode.Tag as string; + if (string.IsNullOrWhiteSpace(txtName.Text)) { + MyMessage.ShowError("Name cannot be empty."); + return; + } + + if (_itemsManager.RenameGroup(selectedPath, txtName.Text)) { btnName.Text = "Change"; + // Selection path will change, ItemsChanged event handles tree refresh & re-selection attempt. + } + else + { + MyMessage.ShowError("Failed to rename. Name might be a duplicate or invalid."); } } private void btnPath_Click(object sender, EventArgs e) { - if (MyMessage.YesNo("Press \"Yes\" to select target file\n" + - "Press \"No\" to select directory path") == DialogResult.Yes) - { - var openFile = new OpenFileDialog - { - Filter = "All files(*.*)|*.*", - Title = Text - }; + if (SelectedNull() || treeOption.SelectedNode.Tag == null) return; + var selectedPath = treeOption.SelectedNode.Tag as string; + var group = _itemsManager.GetGroupInfoByPath(selectedPath); + if (group == null || group.Type != MyEnums.GroupType.Item) return; - if (openFile.ShowDialog() == DialogResult.OK) - { - var filePath = openFile.FileName; - txtPath.Text = filePath; - if (txtName.Text.StartsWith("Header: ") | txtName.Text.StartsWith("Item: ") && string.IsNullOrWhiteSpace(txtIcon.Text)) - { - var fileName = Path.GetFileNameWithoutExtension(filePath); - txtName.Text = fileName; - txtPath.Text = filePath; - txtIcon.Text = filePath; - ItemsManager.UpdateIdentify(treeOption, fileName, filePath, filePath); - return; - } - if (string.IsNullOrWhiteSpace(txtIcon.Text)) - { - txtIcon.Text = filePath; - ItemsManager.ChangeBothPath(treeOption, filePath, filePath); - return; - } + string newFilePath = group.Value.FilePath; + DialogResult choice = MyMessage.YesNoCancel("Select target type:\n" + + "Yes = File\n" + + "No = Directory\n" + + "Cancel = Do Nothing"); + if (choice == DialogResult.Cancel) return; - ItemsManager.ChangePath(treeOption, filePath); + if (choice == DialogResult.Yes) + { + using (var openFile = new OpenFileDialog { Filter = "All files(*.*)|*.*", Title = "Select Target File", FileName = newFilePath ?? "" }) + { + if (openFile.ShowDialog() != DialogResult.OK) return; + newFilePath = openFile.FileName; } - return; } - - var openFolder = new FolderBrowserDialog + else { - ShowNewFolderButton = true - }; + using (var openFolder = new FolderBrowserDialog { ShowNewFolderButton = true, SelectedPath = newFilePath ?? "" }) + { + if (openFolder.ShowDialog() != DialogResult.OK) return; + newFilePath = openFolder.SelectedPath; + } + } - if (openFolder.ShowDialog() == DialogResult.OK) + txtPath.Text = newFilePath; + string newIconPath = txtIcon.Text; + if (string.IsNullOrWhiteSpace(newIconPath) || newIconPath == group.Value.FilePath) // If icon was same as old path or empty { - var folderPath = openFolder.SelectedPath; - txtPath.Text = folderPath; - ItemsManager.ChangePath(treeOption, folderPath); + newIconPath = newFilePath; // Update icon to new path + txtIcon.Text = newIconPath; } + _itemsManager.UpdateGroupValuePaths(selectedPath, newFilePath, newIconPath); } private void btnIcon_Click(object sender, EventArgs e) { - var openFile = new OpenFileDialog - { - Filter = "All files(*.*)|*.*", - Title = Text - }; + if (SelectedNull() || treeOption.SelectedNode.Tag == null) return; + var selectedPath = treeOption.SelectedNode.Tag as string; + var group = _itemsManager.GetGroupInfoByPath(selectedPath); + if (group == null) return; - if (openFile.ShowDialog() == DialogResult.OK) + using (var openFile = new OpenFileDialog { Filter = "Image/Icon/Exe/Dll|*.png;*.jpg;*.jpeg;*.bmp;*.gif;*.ico;*.exe;*.dll|All files (*.*)|*.*", Title = "Select Icon File", FileName = group.Value.IconPath ?? "" }) { - var filePath = openFile.FileName; - txtIcon.Text = filePath; - ItemsManager.ChangeIconPath(treeOption, filePath); + if (openFile.ShowDialog() == DialogResult.OK) + { + txtIcon.Text = openFile.FileName; + _itemsManager.UpdateGroupValuePaths(selectedPath, group.Value.FilePath, openFile.FileName); + } } } - private void btnCancel_Click(object sender, EventArgs e) - { + private void btnCancel_Click(object sender, EventArgs e) { + _settingsService.ReloadTempSettings(); // Revert any changes in TempSettings Application.Exit(); } + #endregion - #endregion Button Events - - #region Utility - - private bool SelectedNull() - { - return treeOption.SelectedNode == null; - } - - #endregion Utility - - #region Speacial Events + #region Utility & Speacial Events (UI State or Detail Panel changes) + private bool SelectedNull() => treeOption.SelectedNode == null; private void txtName_TextChanged(object sender, EventArgs e) { - if (string.IsNullOrWhiteSpace(txtName.Text)) return; + if (SelectedNull() || treeOption.SelectedNode == null || treeOption.SelectedNode.Tag == null || string.IsNullOrWhiteSpace(txtName.Text)) return; btnName.Text = treeOption.SelectedNode.Text == txtName.Text ? "Change" : "Change*"; } private void All_CheckedChanged(object sender, EventArgs e) { - if (chkCMD.Checked) - { - radC.Enabled = true; - radK.Enabled = true; - } - else - { - radC.Enabled = false; - radK.Enabled = false; - } - - SaveTemp(); + radC.Enabled = radK.Enabled = chkCMD.Checked; + SaveTempAdvanceInfo(); } - private void txtCommand_TextChanged(object sender, EventArgs e) - { - SaveTemp(); - } + private void txtCommand_TextChanged(object sender, EventArgs e) => SaveTempAdvanceInfo(); + private void listTarget_SelectedIndexChanged(object sender, EventArgs e) => SaveTempAdvanceInfo(); - private void listTarget_SelectedIndexChanged(object sender, EventArgs e) + private void SaveTempAdvanceInfo() { - SaveTemp(); - } + if (_editor || SelectedNull() || treeOption.SelectedNode.Tag == null) return; + var selectedPath = treeOption.SelectedNode.Tag as string; + if (string.IsNullOrEmpty(selectedPath)) return; - private void SaveTemp() - { - if (_editor) return; - - try + var targetTypes = new List(); + for (int i = 0; i < listTarget.Items.Count; i++) { - var target = new List - { - true, - true, - true, - false - }; - - for (var i = 0; i < listTarget.Items.Count; i++) - { - var selected = listTarget.SelectedIndices; - if (selected.Contains(i)) - { - target[i] = true; - } - else - { - target[i] = false; - } - } - - var type = radC.Checked ? MyEnums.CMDType.C : MyEnums.CMDType.K; - - ItemsManager.UpdateInfo(treeOption, txtCommand.Text, target, chkAdmin.Checked, chkCMD.Checked, type); + targetTypes.Add(listTarget.GetSelected(i)); } - catch + var cmdType = radC.Checked ? MyEnums.CMDType.C : MyEnums.CMDType.K; + _itemsManager.UpdateGroupAdvanceInfo(selectedPath, txtCommand.Text, targetTypes, chkAdmin.Checked, chkCMD.Checked, cmdType); + } + #endregion + + private void FrmSetting_FormClosing(object sender, FormClosingEventArgs e) + { + // Clean up event subscriptions + if (_itemsManager != null) { - // + _itemsManager.ItemsChanged -= ItemsManager_ItemsChanged; } } - - #endregion Speacial Events -} \ No newline at end of file +} diff --git a/Toolkit Launcher/ItemManager/ItemsManager.cs b/Toolkit Launcher/ItemManager/ItemsManager.cs index ed78db9..43f8da1 100644 --- a/Toolkit Launcher/ItemManager/ItemsManager.cs +++ b/Toolkit Launcher/ItemManager/ItemsManager.cs @@ -1,87 +1,91 @@ -using TreeView = System.Windows.Forms.TreeView; +using Toolkit_Launcher.Models; +namespace Toolkit_Launcher.ItemManager; // Changed namespace + +using System; +using System.Collections.Generic; +using System.Linq; +using Toolkit_Launcher; // For MyEnums +using Toolkit_Launcher.Models; // For ListInfo, GroupInfo etc. (already added by script) +using Toolkit_Launcher.Utilities; // For Utility.GetMissingNumber +// TreeView using removed as it's no longer directly manipulated. + +public class ItemsManager // Changed to instance class +{ + private ListInfo _listInfo; + private GroupInfo _lastGroupInfo; // Instance field -namespace Toolkit_Launcher.Helper; + public event EventHandler ItemsChanged; // General event for reloading the tree + // More specific events can be added later if needed (e.g., ItemAdded, ItemRenamed with specific args) -public static class ItemsManager -{ - #region Add Group + public ItemsManager(ListInfo listInfo) + { + _listInfo = listInfo ?? throw new ArgumentNullException(nameof(listInfo)); + } - public static void AddGroup(MyEnums.GroupType type, TreeView treeView, ListInfo? list = null) + protected virtual void OnItemsChanged() { - list ??= PublicClass.TempListInfo; - var selectedNode = treeView.SelectedNode; - var fullPath = selectedNode == null ? "" : treeView.SelectedNode.FullPath; - GroupInfo mainGroup; - var newGroup = new GroupInfo(); - if (string.IsNullOrWhiteSpace(fullPath)) - { - mainGroup = GroupInfo(list, type); - list.ListGroup.Add(mainGroup); - } - else - { - mainGroup = GetGroupInfo(fullPath, list); - newGroup = GroupInfo(mainGroup, type, fullPath); - mainGroup.ListGroup.Add(newGroup); - } + ItemsChanged?.Invoke(this, EventArgs.Empty); + } - if (selectedNode == null) + #region Add Group + public GroupInfo AddGroup(MyEnums.GroupType type, string parentFullPath = null) + { + GroupInfo newGroup; + if (string.IsNullOrWhiteSpace(parentFullPath)) { - treeView.Nodes.Add(Utility.AddGroup(mainGroup, mainGroup.Value.ImageID)); + newGroup = CreateNewGroupInfo(_listInfo, type); + _listInfo.ListGroup.Add(newGroup); } else { - selectedNode.Nodes.Add(Utility.AddSubGroup(newGroup)); - selectedNode.Expand(); + var parentGroup = GetGroupInfoByPath(parentFullPath, _listInfo); + if (parentGroup == null) throw new ArgumentException("Parent group not found", nameof(parentFullPath)); + newGroup = CreateNewGroupInfo(parentGroup, type, parentFullPath); + parentGroup.ListGroup.Add(newGroup); } + OnItemsChanged(); + return newGroup; // Return the new group so UI can select/focus it } + #endregion - #endregion Add Group - - #region Create New Class - - private static GroupInfo GroupInfo(ListInfo list, MyEnums.GroupType type) + #region Create New Class (internal logic, largely unchanged but uses instance _listInfo if needed) + private GroupInfo CreateNewGroupInfo(ListInfo list, MyEnums.GroupType type) { - var index = GetNewIndex(list); + var index = GetNewIndex(list.ListGroup); var name = $"{type}: {index}"; return new GroupInfo { - Identify = Identify(index, name, name), - Value = Value(type), + Identify = CreateIdentify(index, name, name), + Value = CreateValueInfo(type), Type = type, - Advance = Advance() + Advance = CreateAdvanceInfo() }; } - private static GroupInfo GroupInfo(GroupInfo list, MyEnums.GroupType type, string fullPath) + private GroupInfo CreateNewGroupInfo(GroupInfo parentGroup, MyEnums.GroupType type, string parentFullPath) { - var index = GetNewIndex(list); + var index = GetNewIndex(parentGroup.ListGroup); var name = $"{type}: {index}"; - fullPath = $"{fullPath}\\{name}"; + var newFullPath = $"{parentFullPath}\{name}"; return new GroupInfo { - Identify = Identify(index, name, fullPath), - Value = Value(type), + Identify = CreateIdentify(index, name, newFullPath), + Value = CreateValueInfo(type), Type = type, - Advance = Advance() + Advance = CreateAdvanceInfo() }; } - private static Identify Identify(int index, string name, string fullPath) + private Identify CreateIdentify(int index, string name, string fullPath) { - return new Identify - { - Index = index, - Name = name, - FullPath = fullPath - }; + return new Identify { Index = index, Name = name, FullPath = fullPath }; } - private static ValueInfo Value(MyEnums.GroupType type) + private ValueInfo CreateValueInfo(MyEnums.GroupType type) { return new ValueInfo { - ImageID = type switch + ImageID = type switch // This ImageID might need to be re-evaluated or managed by ImageCacheService if it's for UI. { MyEnums.GroupType.Header => 1, MyEnums.GroupType.Item => 2, @@ -91,389 +95,238 @@ private static ValueInfo Value(MyEnums.GroupType type) }; } - private static AdvanceInfo Advance() + private AdvanceInfo CreateAdvanceInfo() { - return new AdvanceInfo - { - TargetTypes = new List - { - true, - true, - false, - false - } - }; + return new AdvanceInfo { TargetTypes = new List { true, true, false, false } }; } - - #endregion Create New Class + #endregion #region Get New Index - - private static int GetNewIndex(ListInfo list) - { - var listIndex = new List(); - listIndex.AddRange(list.ListGroup.Select(group => group.Identify.Index)); - - return Utility.GetMissingNumber(listIndex); - } - - private static int GetNewIndex(GroupInfo list) + private int GetNewIndex(List groupList) { - var listIndex = new List(); - listIndex.AddRange(list.ListGroup.Select(group => group.Identify.Index)); - return Utility.GetMissingNumber(listIndex); + if (!groupList.Any()) return 0; + var listIndex = groupList.Select(group => group.Identify.Index).ToList(); + return Utility.GetMissingNumber(listIndex); // Assuming Utility.GetMissingNumber is static and accessible } - - #endregion Get New Index + #endregion #region Get Class Info - - private static GroupInfo GetParentGroupInfo(string fullPath, ListInfo list) + public GroupInfo GetParentGroupInfoByPath(string fullPath) => GetParentGroupInfoRecursive(fullPath, _listInfo); + private GroupInfo GetParentGroupInfoRecursive(string fullPath, ListInfo list) { - var result = new GroupInfo(); - var index = fullPath.LastIndexOf("\\", StringComparison.Ordinal); - if (index >= 0) - fullPath = fullPath[..index]; + var parentPath = ""; + var lastSeparatorIndex = fullPath.LastIndexOf("\\", StringComparison.Ordinal); // Corrected backslash + if (lastSeparatorIndex >= 0) + parentPath = fullPath.Substring(0, lastSeparatorIndex); + else + return null; // Top-level item, no parent in the context of GroupInfo - foreach (var group in list.ListGroup) - { - if (fullPath == group.Identify.FullPath) - { - return group; - } - result = GetGroupInfoChild(fullPath, group); - if (result.Identify != null!) - { - return result; - } - } - return result; + return GetGroupInfoByPath(parentPath, list); } - private static GroupInfo GetGroupInfo(string fullPath, ListInfo list) + public GroupInfo GetGroupInfoByPath(string fullPath) => GetGroupInfoByPath(fullPath, _listInfo); + private GroupInfo GetGroupInfoByPath(string fullPath, ListInfo list) /* Was GetGroupInfo */ { - var result = new GroupInfo(); foreach (var group in list.ListGroup) { - if (fullPath == group.Identify.FullPath) - { - return group; - } - result = GetGroupInfoChild(fullPath, group); - if (result.Identify != null!) - { - return result; - } + if (fullPath == group.Identify.FullPath) return group; + var foundInChild = GetGroupInfoByPathRecursive(fullPath, group); + if (foundInChild != null) return foundInChild; } - return result; + return null; } - private static GroupInfo GetGroupInfoChild(string fullPath, GroupInfo list) + private GroupInfo GetGroupInfoByPathRecursive(string fullPath, GroupInfo parentGroup) /* Was GetGroupInfoChild */ { - foreach (var group in list.ListGroup) + foreach (var group in parentGroup.ListGroup) { - if (fullPath == group.Identify.FullPath) - { - return group; - } - - var result = GetGroupInfoChild(fullPath, group); - if (result.Identify != null!) - { - return result; - } + if (fullPath == group.Identify.FullPath) return group; + var foundInChild = GetGroupInfoByPathRecursive(fullPath, group); + if (foundInChild != null) return foundInChild; } - return new GroupInfo(); + return null; } - - #endregion Get Class Info + #endregion #region Get Type - - public static MyEnums.GroupType GetType(string fullPath, ListInfo list = null!) - { - list ??= PublicClass.TempListInfo; - foreach (var group in list.ListGroup) - { - if (group.Identify.FullPath == fullPath) - return group.Type; - - if (group.Type == MyEnums.GroupType.Header) - { - var result = GetTypeChild(fullPath, group); - if (GetTypeChild(fullPath, group) != MyEnums.GroupType.Null) - { - return result; - } - } - } - - return MyEnums.GroupType.Null; - } - - private static MyEnums.GroupType GetTypeChild(string fullPath, GroupInfo list) - { - foreach (var group in list.ListGroup) - { - if (group.Identify.FullPath == fullPath) - return group.Type; - - if (group.Type == MyEnums.GroupType.Header) - { - var result = GetTypeChild(fullPath, group); - if (GetTypeChild(fullPath, group) != MyEnums.GroupType.Null) - { - return result; - } - } - } - return MyEnums.GroupType.Null; - } - - #endregion Get Type - - #region Get Detail - - public static GroupInfo GetGroupDetail(TreeView treeView, ListInfo? list = null) + public MyEnums.GroupType GetGroupType(string fullPath) { - list ??= PublicClass.TempListInfo; - var fullPath = treeView.SelectedNode.FullPath; - return GetGroupInfo(fullPath, list); + var group = GetGroupInfoByPath(fullPath, _listInfo); + return group?.Type ?? MyEnums.GroupType.Null; } - - #endregion Get Detail + #endregion #region Remove - - public static void Remove(TreeView treeView, ListInfo? list = null) + public bool RemoveGroup(string fullPath) { - list ??= PublicClass.TempListInfo; - var fullPath = treeView.SelectedNode.FullPath; - if (MyMessage.OkCancel($"Press \"OK\" to confirm the deletion \"{treeView.SelectedNode.Text}\"\n" + - "Press \"Cancel\" to confirm cancellation")) - { - var group = GetGroupInfo(fullPath, list); - Remove(group, list); - treeView.SelectedNode.Remove(); - } - } + var groupToRemove = GetGroupInfoByPath(fullPath, _listInfo); + if (groupToRemove == null) return false; - private static void Remove(GroupInfo groupInfo, ListInfo list) - { - foreach (var group in list.ListGroup) + var parentPath = ""; + var lastSeparatorIndex = fullPath.LastIndexOf("\\", StringComparison.Ordinal); // Corrected backslash + if (lastSeparatorIndex >= 0) { - if (groupInfo.Identify.FullPath == group.Identify.FullPath) + parentPath = fullPath.Substring(0, lastSeparatorIndex); + var parentGroup = GetGroupInfoByPath(parentPath, _listInfo); + if (parentGroup != null) { - list.ListGroup.Remove(groupInfo); - break; - } - if (group.Type == MyEnums.GroupType.Header) - { - var result = RemoveChild(groupInfo, group); - if (result) - { - break; - } + bool removed = parentGroup.ListGroup.Remove(groupToRemove); + if (removed) OnItemsChanged(); + return removed; } } - } - - private static bool RemoveChild(GroupInfo groupInfo, GroupInfo list) - { - foreach (var group in list.ListGroup) + else // Top-level item { - if (groupInfo.Identify.FullPath == group.Identify.FullPath) - { - list.ListGroup.Remove(groupInfo); - return true; - } - if (group.Type == MyEnums.GroupType.Header) - { - var result = RemoveChild(groupInfo, group); - if (result) - { - return true; - } - } + bool removed = _listInfo.ListGroup.Remove(groupToRemove); + if (removed) OnItemsChanged(); + return removed; } - return false; } - - #endregion Remove + #endregion #region Move Up/Down - - public static void MoveUpDown(MyEnums.MoveType move, TreeView treeView, ListInfo? list = null) + public bool MoveGroup(string fullPath, MyEnums.MoveType moveType) { - list ??= PublicClass.TempListInfo; - var fullPath = treeView.SelectedNode.FullPath; - var group = GetGroupInfo(fullPath, list); - MoveUpDown(move, group, list); - Utility.LoadAll(treeView, list); - treeView.SelectedNode.Expand(); - } + List parentList; + var groupToMove = GetGroupInfoByPath(fullPath, _listInfo); + if (groupToMove == null) return false; - private static void MoveUpDown(MyEnums.MoveType move, GroupInfo groupInfo, ListInfo list) - { - for (var i = 0; i < list.ListGroup.Count; i++) + var parentPath = ""; + var lastSeparatorIndex = fullPath.LastIndexOf("\\", StringComparison.Ordinal); // Corrected backslash + if (lastSeparatorIndex >= 0) { - var group = list.ListGroup[i]; - if (groupInfo.Identify.FullPath == group.Identify.FullPath) - { - var oldIndex = list.FindIndex(group.Identify.FullPath); - var newIndex = move.GetNewIndex(oldIndex); - - list.ListGroup.RemoveAt(oldIndex); - if (move == MyEnums.MoveType.MoveDown) - { - list.ListGroup[oldIndex].Identify.Index = oldIndex; - } - else - { - list.ListGroup[newIndex].Identify.Index = oldIndex; - } - group.Identify.Index = newIndex; - - list.ListGroup.Insert(newIndex, group); - break; - } - - if (group.Type == MyEnums.GroupType.Header) - { - var result = MoveUpDownChild(move, groupInfo, group); - if (result) - { - break; - } - } + parentPath = fullPath.Substring(0, lastSeparatorIndex); + var parentGroup = GetGroupInfoByPath(parentPath, _listInfo); + if (parentGroup == null) return false; + parentList = parentGroup.ListGroup; } - } - - private static bool MoveUpDownChild(MyEnums.MoveType move, GroupInfo groupInfo, GroupInfo list) - { - for (var i = 0; i < list.ListGroup.Count; i++) + else { - var group = list.ListGroup[i]; - if (groupInfo.Identify.FullPath == group.Identify.FullPath) - { - var oldIndex = list.FindIndex(group.Identify.FullPath); - var newIndex = move.GetNewIndex(oldIndex); - - list.ListGroup.RemoveAt(oldIndex); - if (move == MyEnums.MoveType.MoveDown) - { - list.ListGroup[oldIndex].Identify.Index = oldIndex; - } - else - { - list.ListGroup[newIndex].Identify.Index = oldIndex; - } - group.Identify.Index = newIndex; - list.ListGroup.Insert(newIndex, group); - break; - } - - var result = MoveUpDownChild(move, groupInfo, group); - if (result) - { - return true; - } + parentList = _listInfo.ListGroup; } - return false; - } - #endregion Move Up/Down + var currentIndex = parentList.IndexOf(groupToMove); + if (currentIndex < 0) return false; - #region Utility + var newIndex = moveType == MyEnums.MoveType.MoveUp ? currentIndex - 1 : currentIndex + 1; - private static GroupInfo _lastGroupInfo = null!; - public static void UpdateIdentify(TreeView treeView, string newName, string path, string iconPath, ListInfo? list = null) - { - list ??= PublicClass.TempListInfo; - var fullPath = treeView.SelectedNode.FullPath; - var group = GetGroupInfo(fullPath, list); - group.Value.FilePath = path; - group.Value.IconPath = iconPath; + if (newIndex < 0 || newIndex >= parentList.Count) return false; - Rename(treeView, group, fullPath, newName, list, false); - } + // Simple swap for now; actual index property update might be more complex if they are not just for ordering + var temp = parentList[newIndex]; + parentList[newIndex] = groupToMove; + parentList[currentIndex] = temp; - public static bool Rename(TreeView treeView, string newName, ListInfo? list = null) - { - list ??= PublicClass.TempListInfo; - var fullPath = treeView.SelectedNode.FullPath; - var group = GetGroupInfo(fullPath, list); - return Rename(treeView, group, fullPath, newName, list); + // Update Identify.Index if it's meant to reflect order + for(int i=0; i g != group && g.Identify.Name == newName); + } + else + { + isDuplicate = _listInfo.ListGroup.Any(g => g != group && g.Identify.Name == newName); + } + if (isDuplicate) { - if (duplicate) - { - MyMessage.ShowError("Found Duplicate " + newName + @", Please Check it again before rename!!!"); - } + // Consider throwing an exception or returning a specific result for duplication return false; } - treeView.SelectedNode.Text = newName; + var oldName = group.Identify.Name; group.Identify.Name = newName; - group.Identify.FullPath = treeView.SelectedNode.FullPath; + + // Update FullPath for the renamed group and all its children + var oldFullPathPrefix = group.Identify.FullPath; + var newFullPath = ""; + if (parentGroup != null) { + newFullPath = $"{parentGroup.Identify.FullPath}\{newName}"; + } else { + newFullPath = newName; + } + group.Identify.FullPath = newFullPath; + UpdateChildFullPaths(group, oldFullPathPrefix, newFullPath); + + OnItemsChanged(); return true; } - public static void ChangePath(TreeView treeView, string path, ListInfo? list = null) + private void UpdateChildFullPaths(GroupInfo parentGroup, string oldParentFullPath, string newParentFullPath) { - list ??= PublicClass.TempListInfo; - var fullPath = treeView.SelectedNode.FullPath; - var group = GetGroupInfo(fullPath, list); - group.Value.FilePath = path; - } + foreach (var child in parentGroup.ListGroup) + { + var oldChildFullPath = child.Identify.FullPath; + // The child's path started with oldParentFullPath, now it needs to start with newParentFullPath + // The relative part of the child's path is oldChildFullPath.Substring(oldParentFullPath.Length) + var relativePath = oldChildFullPath.Substring(oldParentFullPath.Length); + child.Identify.FullPath = $"{newParentFullPath}{relativePath}"; // This assumes direct concatenation, might need separator logic - public static void ChangeIconPath(TreeView treeView, string iconPath, ListInfo? list = null) - { - list ??= PublicClass.TempListInfo; - var fullPath = treeView.SelectedNode.FullPath; - var group = GetGroupInfo(fullPath, list); - group.Value.IconPath = iconPath; - } + // If the child's name itself defined part of the old path, this needs careful handling. + // For now, assuming FullPath is constructed like "ParentPath\ChildName" + // A simpler way if only the name changed: + // child.Identify.FullPath = $"{newParentFullPath}\{child.Identify.Name}"; - public static void ChangeBothPath(TreeView treeView, string path, string iconPath, ListInfo? list = null) - { - list ??= PublicClass.TempListInfo; - var fullPath = treeView.SelectedNode.FullPath; - var group = GetGroupInfo(fullPath, list); - group.Value.FilePath = path; - group.Value.IconPath = iconPath; + + UpdateChildFullPaths(child, oldChildFullPath, child.Identify.FullPath); + } } - public static void UpdateInfo(TreeView treeView, string command, List targetType, bool admin, bool cmd, - MyEnums.CMDType type, ListInfo? list = null) + + public bool UpdateGroupValuePaths(string fullPath, string newFilePath, string newIconPath) { - list ??= PublicClass.TempListInfo; - var fullPath = treeView.SelectedNode.FullPath; - GroupInfo group; - if (_lastGroupInfo != null! && _lastGroupInfo.Identify.FullPath == fullPath) + var group = GetGroupInfoByPath(fullPath, _listInfo); + if (group == null || group.Type == MyEnums.GroupType.Separator) return false; + + bool changed = false; + if (group.Value.FilePath != newFilePath) { - group = _lastGroupInfo; + group.Value.FilePath = newFilePath; + changed = true; } - else + if (group.Value.IconPath != newIconPath) { - group = GetGroupInfo(fullPath, list); + group.Value.IconPath = newIconPath; + changed = true; } + if (changed) OnItemsChanged(); // Or a more specific event like ItemDataChanged + return true; + } - group.Advance.Commands = command; - group.Advance.TargetTypes = targetType; - group.Advance.Admin = admin; - group.Advance.CMD = cmd; - group.Advance.CmdType = type; + public bool UpdateGroupAdvanceInfo(string fullPath, string command, List targetTypes, bool isAdmin, bool isCmd, MyEnums.CMDType cmdType) + { + var group = GetGroupInfoByPath(fullPath, _listInfo); + if (group == null) return false; + + // Cache _lastGroupInfo for potential optimization if UpdateInfo is called repeatedly for the same group + // Though with instance methods, this might be less necessary or handled differently by the caller. _lastGroupInfo = group; + + group.Advance.Commands = command; + group.Advance.TargetTypes = targetTypes; + group.Advance.Admin = isAdmin; + group.Advance.CMD = isCmd; + group.Advance.CmdType = cmdType; + + OnItemsChanged(); // Or a more specific event + return true; } - #endregion Utility -} \ No newline at end of file + #endregion +} diff --git a/Toolkit Launcher/ItemManager/ListInfo.cs b/Toolkit Launcher/Models/ListInfo.cs similarity index 96% rename from Toolkit Launcher/ItemManager/ListInfo.cs rename to Toolkit Launcher/Models/ListInfo.cs index 4e1f663..773353d 100644 --- a/Toolkit Launcher/ItemManager/ListInfo.cs +++ b/Toolkit Launcher/Models/ListInfo.cs @@ -1,5 +1,7 @@ namespace Toolkit_Launcher; +using Toolkit_Launcher; // For MyEnums + public class ListInfo { public List ListGroup { get; set; } = new List(); @@ -40,4 +42,4 @@ public class AdvanceInfo public bool Admin { get; set; } public bool CMD { get; set; } public MyEnums.CMDType CmdType { get; set; } -} \ No newline at end of file +} diff --git a/Toolkit Launcher/Program.cs b/Toolkit Launcher/Program.cs index 4ffa3e3..8180922 100644 --- a/Toolkit Launcher/Program.cs +++ b/Toolkit Launcher/Program.cs @@ -7,7 +7,10 @@ internal static class Program [STAThread] private static void Main(string[] args) { + var settingsService = new Toolkit_Launcher.Services.SettingsService(); + var imageCacheService = new Toolkit_Launcher.Services.ImageCacheService(); ApplicationConfiguration.Initialize(); - Application.Run(FrmMain = new FrmMain(args)); + Application.Run(FrmMain = new FrmMain(args, settingsService, imageCacheService)); + Application.Run(FrmMain = new FrmMain(args, settingsService, imageCacheService)); } -} \ No newline at end of file +} diff --git a/Toolkit Launcher/Services/ImageCacheService.cs b/Toolkit Launcher/Services/ImageCacheService.cs new file mode 100644 index 0000000..a31c382 --- /dev/null +++ b/Toolkit Launcher/Services/ImageCacheService.cs @@ -0,0 +1,120 @@ +namespace Toolkit_Launcher.Services; + +using System.Drawing; +using System.Windows.Forms; +using System.IO; + +public class ImageCacheService +{ + private readonly ImageList _imageList; + private readonly Dictionary _fileImageCache; + + public ImageCacheService() + { + _imageList = new ImageList(); + _fileImageCache = new Dictionary(); + InitializeDefaultImages(); + } + + private void InitializeDefaultImages() + { + // Placeholder for default images - actual images would need to be added to ImageList + // For now, using small dummy bitmaps. + // These indices (0, 1, 2, 3) should correspond to MyEnums.GroupType values if used directly. + _imageList.Images.Add(new Bitmap(16,16)); // 0 - Null + _imageList.Images.Add(new Bitmap(16,16)); // 1 - Header + _imageList.Images.Add(new Bitmap(16,16)); // 2 - Item + _imageList.Images.Add(new Bitmap(16,16)); // 3 - Separator + } + + public Image GetImage(MyEnums.GroupType groupType) + { + int imageIndex = groupType switch + { + MyEnums.GroupType.Header => 1, + MyEnums.GroupType.Item => 2, + MyEnums.GroupType.Separator => 3, + _ => 0 // Default or Null + }; + if (imageIndex >= 0 && imageIndex < _imageList.Images.Count) + { + return _imageList.Images[imageIndex]; + } + return _imageList.Images[0]; // Return default image + } + + private Icon IconFromFilePath(string filePath) + { + Icon result = null; + try + { + if (File.Exists(filePath)) + { + result = Icon.ExtractAssociatedIcon(filePath); + } + } + catch (Exception) + { + // Log error or handle as needed + } + return result; + } + + public Image GetImage(string filePath) + { + if (string.IsNullOrEmpty(filePath)) return GetImage(MyEnums.GroupType.Null); // Return a default image + + if (_fileImageCache.TryGetValue(filePath, out var cachedImage)) + { + return cachedImage; + } + + if (!File.Exists(filePath)) + { + _fileImageCache[filePath] = GetImage(MyEnums.GroupType.Null); // Cache a default "not found" image + return _fileImageCache[filePath]; + } + + Image image = null; + try + { + string extension = Path.GetExtension(filePath)?.ToLowerInvariant(); + if (extension == ".exe" || extension == ".dll") + { + Icon icon = IconFromFilePath(filePath); + if (icon != null) + { + image = icon.ToBitmap(); + } + } + else + { + // For other image types like .png, .jpg, .ico + image = new Bitmap(filePath); + } + } + catch (Exception) + { + // Log error + image = null; // Ensure image is null if loading failed + } + + if (image == null) + { + image = GetImage(MyEnums.GroupType.Null); // Use a default image if loading failed + } + + _fileImageCache[filePath] = image; + return image; + } + + // Provide access to the underlying ImageList for controls that require it (like ContextMenuStrip) + public ImageList GetSystemImageList() + { + // This is tricky, as ImageList is typically populated with images that have keys. + // For now, returning the internal one, but ideally, ContextMenuStrip would also use GetImage(key) + // or icons would be added to this _imageList with specific keys if needed by FrmMain. + // This might need refinement based on how FrmMain uses it. + return _imageList; + } +} diff --git a/Toolkit Launcher/Services/SettingsService.cs b/Toolkit Launcher/Services/SettingsService.cs new file mode 100644 index 0000000..fea6d27 --- /dev/null +++ b/Toolkit Launcher/Services/SettingsService.cs @@ -0,0 +1,62 @@ +using Toolkit_Launcher.Models; +namespace Toolkit_Launcher.Services; + +using Newtonsoft.Json; +using System; +using System.IO; + +public class SettingsService +{ + private readonly string _settingsFilePath; + public ListInfo CurrentSettings { get; private set; } + public ListInfo TempSettings { get; set; } // Used for editing in settings UI + + public SettingsService(string settingsFileName = "setting.json") + { + _settingsFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, settingsFileName); + CurrentSettings = LoadSettingsFromFile(); + TempSettings = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(CurrentSettings)); // Deep copy for temporary edits + } + + private ListInfo LoadSettingsFromFile() + { + if (!File.Exists(_settingsFilePath)) + { + return new ListInfo(); // Return default/empty settings + } + + try + { + var jsonData = File.ReadAllText(_settingsFilePath); + return JsonConvert.DeserializeObject(jsonData) ?? new ListInfo(); + } + catch (Exception ex) + { + // Consider logging this error appropriately + Console.WriteLine($"Error loading settings: {ex.Message}"); + return new ListInfo(); // Return default/empty on error + } + } + + public void SaveSettings() + { + try + { + // Save TempSettings to CurrentSettings and then to file + CurrentSettings = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(TempSettings)); // Deep copy back + var jsonData = JsonConvert.SerializeObject(CurrentSettings, Formatting.Indented); + File.WriteAllText(_settingsFilePath, jsonData); + } + catch (Exception ex) + { + // Consider logging this error appropriately + Console.WriteLine($"Error saving settings: {ex.Message}"); + } + } + + public void ReloadTempSettings() + { + // Reload TempSettings from CurrentSettings (e.g., when settings dialog is cancelled) + TempSettings = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(CurrentSettings)); + } +} diff --git a/Toolkit Launcher/UI/Helpers/TreeViewHelper.cs b/Toolkit Launcher/UI/Helpers/TreeViewHelper.cs new file mode 100644 index 0000000..d7c24c1 --- /dev/null +++ b/Toolkit Launcher/UI/Helpers/TreeViewHelper.cs @@ -0,0 +1,145 @@ +using Toolkit_Launcher.Models; +namespace Toolkit_Launcher.UI.Helpers; + +using System.Windows.Forms; +using Toolkit_Launcher.Services; // For ImageCacheService +using Toolkit_Launcher.ItemManager; // For ItemsManager namespace +using Toolkit_Launcher; // Added for ListInfo, GroupInfo, MyEnums + +public static class TreeViewHelper +{ + public static void LoadAll(TreeView treeView, ListInfo listInfo, ImageCacheService imageCacheService) + { + string selectedNodePath = treeView.SelectedNode?.Tag as string; // Use Tag for path + + treeView.BeginUpdate(); + treeView.Nodes.Clear(); + treeView.ImageList?.Images.Clear(); // Clear existing images if any, or manage them better + + if (listInfo == null) + { + treeView.EndUpdate(); + return; + } + + // Ensure default images are in the ImageList for consistency if ImageCacheService provides them this way + // This part depends on how ImageCacheService and ImageList are designed to work together. + // For now, assuming GetImageIndex will add images as needed. + // A more robust approach would be to pre-populate ImageList from ImageCacheService for known types. + + + foreach (var group in listInfo.ListGroup) + { + treeView.Nodes.Add(CreateTreeNode(group, imageCacheService, treeView.ImageList)); + } + treeView.EndUpdate(); + + if (!string.IsNullOrEmpty(selectedNodePath)) + { + SelectNodeByPath(treeView, selectedNodePath); + } + } + + private static TreeNode CreateTreeNode(GroupInfo groupInfo, ImageCacheService imageCacheService, ImageList imageList) + { + Image nodeImage = null; + if (!string.IsNullOrEmpty(groupInfo.Value.IconPath) && groupInfo.Type != MyEnums.GroupType.Header) // Headers might use type-based icon + { + nodeImage = imageCacheService.GetImage(groupInfo.Value.IconPath); + } + + if (nodeImage == null || nodeImage == imageCacheService.GetImage(MyEnums.GroupType.Null)) // If no specific icon, or specific icon failed to load + { + nodeImage = imageCacheService.GetImage(groupInfo.Type); + } + + var imageIndex = GetImageIndex(imageList, nodeImage, groupInfo.Identify.FullPath); // Pass a key for the image + + var treeNode = new TreeNode(groupInfo.Identify.Name, imageIndex, imageIndex) + { + Tag = groupInfo.Identify.FullPath // Store full path in Tag for easy retrieval + }; + + foreach (var subGroup in groupInfo.ListGroup) + { + treeNode.Nodes.Add(CreateTreeNode(subGroup, imageCacheService, imageList)); + } + return treeNode; + } + + private static int GetImageIndex(ImageList imageList, Image image, string key) + { + if (image == null) return 0; // Default image index if truly null + if (imageList == null) return -1; // Should not happen if treeView has an ImageList + + if (imageList.Images.ContainsKey(key)) + { + return imageList.Images.IndexOfKey(key); + } + else + { + imageList.Images.Add(key, image); + return imageList.Images.Count - 1; + } + } + + + public static void SelectNodeByPath(TreeView treeView, string path) + { + foreach (TreeNode node in treeView.Nodes) + { + var foundNode = FindNodeRecursive(node, path); + if (foundNode != null) + { + treeView.SelectedNode = foundNode; + foundNode.EnsureVisible(); + treeView.Focus(); + break; + } + } + } + + private static TreeNode FindNodeRecursive(TreeNode parentNode, string path) + { + if ((string)parentNode.Tag == path) + { + return parentNode; + } + foreach (TreeNode childNode in parentNode.Nodes) + { + var found = FindNodeRecursive(childNode, path); + if (found != null) return found; + } + return null; + } + + public static TreeNode AddTreeNode(TreeView treeView, GroupInfo newGroupInfo, ImageCacheService imageCacheService, string parentNodePath = null) + { + TreeNode newNode = CreateTreeNode(newGroupInfo, imageCacheService, treeView.ImageList); + TreeNode parentNode = null; + + if (!string.IsNullOrEmpty(parentNodePath)) + { + // FindNodeRecursive needs to search all root nodes if parentNodePath is multi-level. + // This simplified version assumes parentNodePath is directly under a root node or is a root node's child. + // For deeply nested, a more robust search from treeView.Nodes might be needed. + foreach(TreeNode rootNode in treeView.Nodes) { + parentNode = FindNodeRecursive(rootNode, parentNodePath); + if (parentNode != null) break; + } + } + + if (parentNode != null) + { + parentNode.Nodes.Add(newNode); + parentNode.Expand(); + } + else + { + treeView.Nodes.Add(newNode); + } + treeView.SelectedNode = newNode; + newNode.EnsureVisible(); + return newNode; + } +} diff --git a/Toolkit Launcher/Utilities/PublicClass.cs b/Toolkit Launcher/Utilities/PublicClass.cs deleted file mode 100644 index 314f486..0000000 --- a/Toolkit Launcher/Utilities/PublicClass.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Toolkit_Launcher; - -public static class PublicClass -{ - public static ListInfo ListInfo = new ListInfo(), TempListInfo = new ListInfo(); - public static readonly string PATH = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "setting.json"); - public static ImageList ImageList = new ImageList(); -} \ No newline at end of file diff --git a/Toolkit Launcher/Utilities/Utility.cs b/Toolkit Launcher/Utilities/Utility.cs index 4ca404a..4d2507c 100644 --- a/Toolkit Launcher/Utilities/Utility.cs +++ b/Toolkit Launcher/Utilities/Utility.cs @@ -1,125 +1,28 @@ -using TreeView = System.Windows.Forms.TreeView; +// using TreeView = System.Windows.Forms.TreeView; // REMOVED -namespace Toolkit_Launcher; +// Namespace and other necessary using statements (System, System.Collections.Generic, System.IO, System.Linq) +// should be preserved if they were there and are still needed by remaining methods. +// Assuming they are part of the original file structure. +using Toolkit_Launcher.Models; +namespace Toolkit_Launcher.Utilities; // Ensure this is correct -public static class Utility -{ - public static TreeNode AddGroup(GroupInfo groupInfo, int image) - { - var node = new TreeNode(groupInfo.Identify.Name, image, image); - - foreach (var group in groupInfo.ListGroup) - { - if (group.Type == MyEnums.GroupType.Header) - { - node.Nodes.Add(AddGroup(group, image)); - } - else - { - node.Nodes.Add(group.Type.ToString(), group.Identify.Name, image); - } - } - - return node; - } +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; - public static TreeNode AddSubGroup(GroupInfo groupInfo) - { - var node = new TreeNode(groupInfo.Identify.Name, groupInfo.Value.ImageID, groupInfo.Value.ImageID); - return node; - } - - public static void LoadAll(TreeView treeView, ListInfo listInfo) - { - var tempNode = ""; - var isSelected = treeView.SelectedNode == null; - if (!isSelected) - { - tempNode = treeView.SelectedNode.FullPath; - } - treeView.Nodes.Clear(); - - foreach (var group in listInfo.ListGroup) - { - if (group.Type == MyEnums.GroupType.Header) - { - treeView.Nodes.Add(LoadAll(group)); - } - else - { - treeView.Nodes.Add(group.Type.ToString(), group.Identify.Name, group.Value.ImageID); - } - } - - if (!isSelected) - { - SelectNode(treeView, tempNode); - } - } - - public static TreeNode LoadAll(GroupInfo groupInfo) - { - var node = new TreeNode(groupInfo.Identify.Name, groupInfo.Value.ImageID, groupInfo.Value.ImageID); - - foreach (var group in groupInfo.ListGroup) - { - if (group.Type == MyEnums.GroupType.Header) - { - node.Nodes.Add(LoadAll(group)); - } - else - { - node.Nodes.Add(group.Type.ToString(), group.Identify.Name, group.Value.ImageID); - } - } - - return node; - } - - private static void SelectNode(TreeView treeView, string oldNode) - { - foreach (var obj in treeView.Nodes) - { - var node = (TreeNode)obj; - if (node.FullPath == oldNode) - { - treeView.SelectedNode = node; - break; - } - var result = SelectNodeChild(node, treeView, oldNode); - if (result) - { - break; - } - } - } - - private static bool SelectNodeChild(TreeNode treeNode, TreeView treeView, string oldNode) - { - foreach (var obj in treeNode.Nodes) - { - var node = (TreeNode)obj; - if (node.FullPath == oldNode) - { - treeView.SelectedNode = node; - return true; - } - var result = SelectNodeChild(node, treeView, oldNode); - if (result) - { - return true; - } - } - - return false; - } +public static class Utility +{ + // TreeView-specific methods like AddGroup, AddSubGroup, LoadAll (both overloads), + // SelectNode, SelectNodeChild have been REMOVED. public static int GetMissingNumber(List listNumber) { try { - var heightValue = listNumber.Max(t => t) + 2; + if (listNumber == null || !listNumber.Any()) return 0; // Handle empty or null list + var heightValue = listNumber.Max() + 2; // Max() will throw on empty list if not checked var result = Enumerable.Range(0, heightValue).Except(listNumber); return result.ElementAt(0); } @@ -131,11 +34,13 @@ public static int GetMissingNumber(List listNumber) public static int FindIndex(this ListInfo list, string contains) { + if (list == null) return -1; return list.ListGroup.FindIndex(info => info.Identify.FullPath.Contains(contains)); } public static int FindIndex(this GroupInfo list, string contains) { + if (list == null) return -1; return list.ListGroup.FindIndex(info => info.Identify.FullPath.Contains(contains)); } @@ -144,56 +49,18 @@ public static int GetNewIndex(this MyEnums.MoveType move, int index) return move == MyEnums.MoveType.MoveUp ? index - 1 : index + 1; } - public static Icon IconFromFilePath(string filePath) - { - var result = (Icon)null!; - try - { - if (!File.Exists(filePath)) - { - return result; - } - - result = Icon.ExtractAssociatedIcon(filePath); - } - catch (Exception) - { - // swallow and return nothing. You could supply a default Icon here as well - } - - return result!; - } - - public static Image GetImage(this string? filePath) + public static MyEnums.TargetType GetTarget(this string value) { - if (filePath == null) return null!; - if (!File.Exists(filePath)) return null!; - if (filePath.EndWith(".exe") || filePath.EndWith(".dll")) + if (string.IsNullOrEmpty(value) || !File.Exists(value) && !Directory.Exists(value)) { - return IconFromFilePath(filePath).ToBitmap(); + return MyEnums.TargetType.All; // Or handle as an error/unknown } - return new Bitmap(filePath); - } - public static Image GetImage(this MyEnums.GroupType type) - { - var image = type switch - { - MyEnums.GroupType.Header => 1, - MyEnums.GroupType.Item => 2, - MyEnums.GroupType.Separator => 3, - _ => 0 - }; - return PublicClass.ImageList.Images[image]; - } - - public static MyEnums.TargetType GetTarget(this string value) - { - var attr = File.GetAttributes(value); + FileAttributes attr = File.GetAttributes(value); if (attr.HasFlag(FileAttributes.Directory)) return MyEnums.TargetType.Directory; - return Path.GetExtension(value) switch + return Path.GetExtension(value)?.ToLowerInvariant() switch // Added ToLowerInvariant for consistency { ".exe" => MyEnums.TargetType.Exe, ".dll" => MyEnums.TargetType.Dll, @@ -203,13 +70,15 @@ public static MyEnums.TargetType GetTarget(this string value) public static bool IsTarget(this MyEnums.TargetType targetType, List enable) { + if (enable == null || enable.Count < 4) return false; // Basic validation + return targetType switch { MyEnums.TargetType.Exe => enable[0], MyEnums.TargetType.Dll => enable[1], MyEnums.TargetType.Directory => enable[2], - MyEnums.TargetType.All => enable[3], + MyEnums.TargetType.All => enable[3], // Assuming index 3 is for 'All' _ => false }; } -} \ No newline at end of file +}