using ModpackUpdater.Apps.Manager.Api.Model; using ModpackUpdater.Apps.Manager.Api.Plugins.Features; using ModpackUpdater.Apps.Manager.Api.Plugins.Params; using ModpackUpdater.Apps.Manager.LangRes; using ModpackUpdater.Apps.Manager.Settings; using Pilz.Features; using Pilz.UI.Symbols; using Pilz.UI.WinForms.Extensions; using Pilz.UI.WinForms.Telerik.Features; using Telerik.WinControls; using Telerik.WinControls.UI; namespace ModpackUpdater.Apps.Manager.Ui; public partial class MainForm : RadForm, IMainApi { private record RecentFilesItemTag(WorkspaceConfig Config, WorkspaceFeature Feature); private record WorkspaceTag(IWorkspace Workspace, WorkspaceFeature Feature); private bool loadingMainView; private WorkspaceTag? wsInfo; private InstallAction? tempAction; Form IMainApi.MainWindow => this; IWorkspace? IMainApi.CurWorkspace => wsInfo?.Workspace; public IActionSetInfos? CurActionSet => radTreeView_Sets.SelectedNode?.Tag as IActionSetInfos; public MainForm() { InitializeComponent(); radMenuItem_Workspace.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.workspace, SymbolSize.Small); radMenuItem_WorkspacePreferences.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.settings, SymbolSize.Small); radMenuItem_SaveWorkspace.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.save, SymbolSize.Small); //radMenuItem_OpenNewWorkspace.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.new_window, SymbolSize.Small); //radMenuItem_RecentWorkspaces.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.time_machine, SymbolSize.Small); radMenuItem_Updates.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.update_done, SymbolSize.Small); radMenuItem_CreateUpdate.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.add, SymbolSize.Small); radMenuItem_RemoveUpdate.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.remove, SymbolSize.Small); radMenuItem_Tools.SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.tools, SymbolSize.Small); radMenuItem_SaveWorkspace.Shortcuts.Add(new(Keys.Control, Keys.S)); PluginFeatureController.Instance.Features.Get(FeatureTypes.Workspace).InsertItemsTo(radMenuItem_Workspace.Items, customClickHandler: RadMenuItem_OpenNewWorkspace_Click, insertPrioSplitters: true, customDefault: radMenuItem_Workspace.Items.IndexOf(radMenuHeaderItem_NewWorkspace) + 1); PluginFeatureController.Instance.Functions.Get(FeatureTypes.Tools).InsertItemsTo(radMenuItem_Tools.Items, customClickHandler: RadMenuItem_ToolsItem_Click, insertPrioSplitters: true); } private void LoadRecentWorkspaces() { var settings = Program.Settings.Get(); var startIndex = radMenuItem_Workspace.Items.IndexOf(radMenuHeaderItem_RecentWorkspaces) + 1; foreach (var item in radMenuItem_Workspace.Items.Where(n => n.Tag is RecentFilesItemTag).ToArray()) radMenuItem_Workspace.Items.Remove(item); foreach (var config in settings.Workspaces) { if (PluginFeatureController.Instance.Features.Get(FeatureTypes.Workspace).OfType().FirstOrDefault(n => n.Identifier == config.ProviderId) is not WorkspaceFeature feature) continue; var item = new RadMenuItem { Text = config.DisplayText, Tag = new RecentFilesItemTag(config, feature), SvgImage = feature.Icon as RadSvgImage, }; item.Click += RadMenuItem_OpenRecentWorkspace_Click; radMenuItem_Workspace.Items.Insert(startIndex++, item); } } private void AddToRecentFiles(IWorkspace workspace) { var settings = Program.Settings.Get(); settings.Workspaces.Remove(workspace.Config); settings.Workspaces.Insert(0, workspace.Config); while (settings.Workspaces.Count > 20) settings.Workspaces.RemoveAt(20); } private async Task LoadNewWorkspace(IWorkspace? workspace, WorkspaceFeature feature) { if (workspace is null) return; if (workspace != wsInfo?.Workspace) wsInfo = new(workspace, feature); AddToRecentFiles(workspace); Invoke(LoadRecentWorkspaces); radWaitingBar_Updates.StartWaiting(); if (!await workspace.Load()) { radWaitingBar_Updates.StopWaiting(); return; } Invoke(LoadWorkspace); } private void LoadWorkspace() { if (wsInfo?.Workspace.Config is null || wsInfo.Workspace.InstallInfos is null || wsInfo.Workspace.UpdateInfos is null) return; radWaitingBar_Updates.StartWaiting(); Text = wsInfo.Workspace.Config.DisplayText; radTreeView_Sets.BeginUpdate(); radTreeView_Sets.Nodes.Clear(); AddUpdateItem(wsInfo.Workspace.InstallInfos); var nodeUpdates = new RadTreeNode { Text = "Updates", Name = "updates", SvgImage = AppGlobals.Symbols.GetSvgImage(AppSymbols.update_done, SymbolSize.Small), }; wsInfo.Workspace.UpdateInfos.Updates.ForEach(n => AddUpdateItem(n, nodeUpdates.Nodes)); radTreeView_Sets.Nodes.Add(nodeUpdates); radTreeView_Sets.EndUpdate(); radWaitingBar_Updates.StopWaiting(); } private RadTreeNode AddUpdateItem(IActionSetInfos infos) { return AddUpdateItem(infos, radTreeView_Sets.Nodes); } private RadTreeNode AddUpdateItem(IActionSetInfos infos, RadTreeNodeCollection parent) { var item = CreateUpdateItem(infos); parent.Add(item); return item; } private RadTreeNode InsertUpdateItem(IActionSetInfos infos) { var item = CreateUpdateItem(infos); var nodeUpdates = radTreeView_Sets.Nodes["updates"]; nodeUpdates.Nodes.Insert(Math.Min(1, nodeUpdates.Nodes.Count), item); return item; } private RadTreeNode CreateUpdateItem(IActionSetInfos infos) { var item = new RadTreeNode(); UpdateUpdateItem(item, infos); return item; } private void UpdateUpdateItem(RadTreeNode item) { if (item.Tag is IActionSetInfos infos) UpdateUpdateItem(item, infos); } private void SaveActionsSet() { if (loadingMainView || CurActionSet is not IActionSetInfos infos) return; if (!Version.TryParse(radTextBoxControl_Version.Text.Trim(), out Version? version)) version = new Version("1.0.0.0"); infos.Version = version; infos.IsPublic = radCheckBox_IsPublic.Checked; UpdateUpdateItem(radTreeView_Sets.SelectedNode); } private void UpdateUpdateItem(RadTreeNode item, IActionSetInfos infos) { if (item.Tag != infos) item.Tag = infos; if (infos is UpdateInfo) item.Text = string.Format(GeneralLangRes.Node_Update, infos.Version?.ToString() ?? "?"); else if (infos is InstallInfos) item.Text = string.Format(GeneralLangRes.Node_Install, infos.Version?.ToString() ?? "?"); else item.Text = infos.Version.ToString(); item.SvgImage = AppGlobals.Symbols.GetSvgImage(infos.IsPublic ? AppSymbols.eye : AppSymbols.invisible, SymbolSize.Small); } public void UpdateItem(IActionSetInfos actionSetInfos) { RadTreeNode? item = null; var nodeUpdates = radTreeView_Sets.Nodes["updates"]; foreach (var iitem in nodeUpdates.Nodes) { if (item == null && iitem.Value == actionSetInfos) item = iitem; } if (item == null) InsertUpdateItem(actionSetInfos); else if (wsInfo?.Workspace.UpdateInfos != null && !wsInfo.Workspace.UpdateInfos.Updates.Contains(actionSetInfos)) item.Remove(); else UpdateUpdateItem(item); } public void UpdateItem(InstallAction action) { foreach (var row in radGridView_Actions.Rows) { if (row.Tag == action) UpdateActionRow(row); } } private void LoadMainView() { loadingMainView = true; tableLayoutPanel_ActionSet.Visible = false; tableLayoutPanel_ActionSetInfo.Visible = false; if (CurActionSet is IActionSet set) { if (set is IActionSetInfos infos) { radTextBoxControl_Version.Text = infos.Version?.ToString(); radCheckBox_IsPublic.Checked = infos.IsPublic; tableLayoutPanel_ActionSetInfo.Visible = true; } tableLayoutPanel_ActionSet.Visible = true; LoadActionSet(set); } loadingMainView = false; } private void LoadActionSet(IActionSet infos) { radGridView_Actions.BeginUpdate(); radGridView_Actions.Rows.Clear(); radGridView_Actions.Columns.Clear(); // Add columns radGridView_Actions.Columns.AddRange([ new GridViewTextBoxColumn { Name = "id", HeaderText = ActionsListLangRes.Col_Id, Width = 150, IsVisible = infos is not UpdateInfo, IsPinned = true, }, new GridViewTextBoxColumn { Name = "name", HeaderText = ActionsListLangRes.Col_Name, Width = 150, IsVisible = infos is not UpdateInfo, }, new GridViewTextBoxColumn { Name = "inherit", HeaderText = ActionsListLangRes.Col_InheritFrom, Width = 150, IsVisible = infos is UpdateInfo, IsPinned = true, }, new GridViewComboBoxColumn { Name = "utype", HeaderText = ActionsListLangRes.Col_UpdateType, Width = 150, DataSource = Enum.GetValues(), IsVisible = infos is UpdateInfo, }, new GridViewComboBoxColumn { Name = "side", HeaderText = ActionsListLangRes.Col_Side, Width = 100, DataSource = Enum.GetValues(), }, new GridViewCheckBoxColumn { Name = "isextra", HeaderText = ActionsListLangRes.Col_IsExtra, Width = 50, }, new GridViewCheckBoxColumn { Name = "iszip", HeaderText = ActionsListLangRes.Col_IsZip, Width = 50, }, new GridViewCheckBoxColumn { Name = "isdir", HeaderText = ActionsListLangRes.Col_IsDir, Width = 50, IsVisible = infos is UpdateInfo, }, new GridViewTextBoxColumn { Name = "destpath", HeaderText = ActionsListLangRes.Col_DestPath, Width = 250, }, new GridViewComboBoxColumn { Name = "srctype", HeaderText = ActionsListLangRes.Col_SrcType, Width = 150, DataSource = Enum.GetValues(), }, new GridViewTextBoxColumn { Name = "srcowner", HeaderText = ActionsListLangRes.Col_SrcOwner, Width = 150, }, new GridViewTextBoxColumn { Name = "srcname", HeaderText = ActionsListLangRes.Col_SrcName, Width = 150, }, new GridViewTextBoxColumn { Name = "srcregex", HeaderText = ActionsListLangRes.Col_SrcRegEx, Width = 200, }, new GridViewTextBoxColumn { Name = "srctag", HeaderText = ActionsListLangRes.Col_SrcTag, Width = 150, }, new GridViewTextBoxColumn { Name = "srcurl", HeaderText = ActionsListLangRes.Col_SrcUrl, Width = 350, }, new GridViewTextBoxColumn { Name = "zippath", HeaderText = ActionsListLangRes.Col_ZipPath, Width = 200, }, new GridViewTextBoxColumn { Name = "srcpath", HeaderText = ActionsListLangRes.Col_SrcPath, Width = 250, IsVisible = infos is UpdateInfo, }, new GridViewTextBoxColumn { Name = "website", HeaderText = ActionsListLangRes.Col_Website, Width = 350, }, ]); // Add rows foreach (var action in infos.Actions) { var row = radGridView_Actions.Rows.AddNew(); row.Tag = action; UpdateActionRow(row); } radGridView_Actions.EndUpdate(); } private void UpdateActionRow(GridViewRowInfo row) { if (row.Tag is not InstallAction action) return; row.Cells["id"].Value = action.Id; row.Cells["name"].Value = action.Name; row.Cells["iszip"].Value = action.IsZip; row.Cells["zippath"].Value = action.ZipPath; row.Cells["destpath"].Value = action.DestPath; row.Cells["srcurl"].Value = action.SourceUrl; row.Cells["srctype"].Value = action.SourceType; row.Cells["srcowner"].Value = action.SourceOwner; row.Cells["srcname"].Value = action.SourceName; row.Cells["srcregex"].Value = action.SourceRegex; row.Cells["srctag"].Value = action.SourceTag; row.Cells["side"].Value = action.Side; row.Cells["isextra"].Value = action.IsExtra; row.Cells["website"].Value = action.Website; if (action is not UpdateAction uaction) return; row.Cells["inherit"].Value = uaction.InheritFrom; // TODO: Find inherit action and put it in here! row.Cells["utype"].Value = uaction.Type; row.Cells["srcpath"].Value = uaction.SrcPath; row.Cells["isdir"].Value = uaction.IsDirectory; } private void Form1_Load(object sender, EventArgs e) { LoadRecentWorkspaces(); } private async void RadMenuItem_OpenNewWorkspace_Click(object? sender, EventArgs e) { if (sender is RadMenuItem item && item.Tag is WorkspaceFeature feature) { var ws = wsInfo?.Workspace; if (feature.Configure(ws)) await LoadNewWorkspace(ws, feature); } } private async void RadMenuItem_OpenRecentWorkspace_Click(object? sender, EventArgs e) { if (sender is RadMenuItem item && item.Tag is RecentFilesItemTag tag && tag.Feature.CreateFromConfig(tag.Config) is IWorkspace workspace) await LoadNewWorkspace(workspace, tag.Feature); } private async void RadMenuItem_WorkspacePreferences_Click(object sender, EventArgs e) { if (wsInfo != null) { var ws = wsInfo.Workspace; if (wsInfo.Feature.Configure(ws)) await LoadNewWorkspace(ws, wsInfo.Feature); } } private void RadMenuItem_SaveWorkspace_Click(object sender, EventArgs e) { wsInfo?.Workspace.Save(); } private void RadMenuItem_ToolsItem_Click(object? sender, EventArgs e) { if (sender is RadMenuItem item && item.Tag is PluginFunction func) func.Execute(new MainApiParameters(this)); } private void RadTreeView_Sets_SelectedNodeChanged(object sender, RadTreeViewEventArgs e) { LoadMainView(); } private void RadGridView_Actions_CellFormatting(object sender, CellFormattingEventArgs e) { //var cellElement = e.CellElement; //var cellInfo = e.Row.Cells[e.Column.Name]; //if (e.Column.Name == "srctype" && cellInfo?.Value is string sourceTypeStr && Enum.Parse(sourceTypeStr) is SourceType sourceType) //{ // cellElement.SvgImage = sourceType switch // { // SourceType.DirectLink => AppGlobals.Symbols.GetSvgImage(AppSymbols.link, SymbolSize.Small), // SourceType.GitHub => AppGlobals.Symbols.GetSvgImage(AppSymbols.github, SymbolSize.Small), // _ => null, // }; // cellElement.DrawImage = cellElement.SvgImage != null; // cellElement.TextImageRelation = TextImageRelation.ImageBeforeText; // cellElement.ImageAlignment = ContentAlignment.MiddleLeft; //} //else //{ // cellElement.ResetValue(LightVisualElement.SvgImageProperty, ValueResetFlags.Local); // cellElement.ResetValue(LightVisualElement.DrawImageProperty, ValueResetFlags.Local); // cellElement.ResetValue(LightVisualElement.TextImageRelationProperty, ValueResetFlags.Local); // cellElement.ResetValue(LightVisualElement.ImageAlignmentProperty, ValueResetFlags.Local); //} } private void RadGridView_Actions_CellValueChanged(object sender, GridViewCellEventArgs e) { if (e.Row is null) return; if (e.Row.Tag is not InstallAction action) { if (CurActionSet is UpdateInfo) action = tempAction ??= new UpdateAction(); else if (CurActionSet is InstallInfos) action = tempAction ??= new InstallAction(); else return; } var uaction = action as UpdateAction; var newValue = e.Row.Cells[e.Column.Name].Value; var colName = e.Column.Name; if (newValue is bool valueBool) { switch (colName) { case "iszip": action.IsZip = valueBool; break; case "isextra": action.IsExtra = valueBool; break; case "isdir": if (uaction is not null) uaction.IsDirectory = valueBool; break; } } else { var valueStr = newValue as string ?? string.Empty; var valueNullStr = valueStr.Nullify(); switch (colName) { case "id": action.Id = valueNullStr; break; case "name": action.Name = valueNullStr; break; case "zippath": action.ZipPath = valueNullStr; break; case "destpath": action.DestPath = valueNullStr; break; case "srcurl": action.SourceUrl = valueNullStr; break; case "srctype": action.SourceType = Enum.Parse(valueStr); break; case "srcowner": action.SourceOwner = valueNullStr; break; case "srcname": action.SourceName = valueNullStr; break; case "srcregex": action.SourceRegex = valueNullStr; break; case "srctag": action.SourceTag = valueNullStr; break; case "website": action.Website = valueNullStr; break; case "side": action.Side = Enum.Parse(valueStr); break; case "inherit": if (uaction is not null) uaction.InheritFrom = valueNullStr; break; case "utype": if (uaction is not null) uaction.Type = Enum.Parse(valueStr); break; case "srcpath": if (uaction is not null) uaction.SrcPath = valueNullStr; break; } } } private void RadMenuItem_Updates_DropDownOpening(object sender, System.ComponentModel.CancelEventArgs e) { radMenuItem_RemoveUpdate.Enabled = radTreeView_Sets.SelectedNode?.Tag is UpdateInfo; } private void RadMenuItem_CreateUpdate_Click(object sender, EventArgs e) { if (wsInfo?.Workspace.UpdateInfos is null) return; var infos = new UpdateInfo { Version = wsInfo.Workspace.InstallInfos?.Version, }; wsInfo.Workspace.UpdateInfos.Updates.Insert(0, infos); InsertUpdateItem(infos); } private void RadMenuItem_RemoveUpdate_Click(object sender, EventArgs e) { if (radTreeView_Sets.SelectedNode?.Tag is UpdateInfo infos && wsInfo?.Workspace.UpdateInfos is not null && RadMessageBox.Show(MsgBoxLangRes.RemoveUpdate, MsgBoxLangRes.RemoveUpdate_Title, MessageBoxButtons.YesNo, RadMessageIcon.Exclamation).IsYes()) { wsInfo.Workspace.UpdateInfos.Updates.Remove(infos); radTreeView_Sets.SelectedNode.Remove(); } } private void RadGridView_Actions_UserAddedRow(object sender, GridViewRowEventArgs e) { if (tempAction is UpdateAction uaction && CurActionSet is UpdateInfo uinfo) uinfo.Actions.Add(uaction); else if (tempAction is InstallAction iaction && CurActionSet is InstallInfos iinfo) iinfo.Actions.Add(iaction); tempAction = null; } private void RadGridView_Actions_UserDeletingRow(object sender, GridViewRowCancelEventArgs e) { foreach (var row in e.Rows) { if (row.Tag is UpdateAction uaction && CurActionSet is UpdateInfo uinfo) uinfo.Actions.Remove(uaction); else if (row.Tag is InstallAction iaction && CurActionSet is InstallInfos iinfo) iinfo.Actions.Remove(iaction); } } private void RadGridView_Actions_ContextMenuOpening(object sender, ContextMenuOpeningEventArgs e) { if (e.ContextMenuProvider is GridDataCellElement) { e.ContextMenu.Items.Add(new RadMenuSeparatorItem()); PluginFeatureController.Instance.Functions.Get(FeatureTypes.ActionsContextMenu).InsertItemsTo(e.ContextMenu.Items, customClickHandler: RadMenuItem_ToolsItem_Click, insertPrioSplitters: true); } } private void RadTextBoxControl1_TextChanged(object sender, EventArgs e) { SaveActionsSet(); } private void RadCheckBox1_ToggleStateChanged(object sender, StateChangedEventArgs args) { SaveActionsSet(); } }