/// <summary> /// Save the changes. /// Similar to adding, except we have to remove the old instance, /// and there's an API specifically for renaming. /// </summary> protected override void Save() { if (repoEditList != null) { // Copy the temp list of repositories to the registry registry.Repositories = repoEditList; RegistryManager.Instance(ksp).Save(); } if (compatEditList != null) { ksp.SetCompatibleVersions(compatEditList); } string oldName = KSPListScreen.InstallName(manager, ksp); if (path.Value != ksp.GameDir()) { // If the path is changed, then we have to remove the old instance // and replace it with a new one, whether or not the name is changed. manager.RemoveInstance(oldName); manager.AddInstance(name.Value, new KSP(path.Value, new NullUser())); } else if (name.Value != oldName) { // If only the name changed, there's an API for that. manager.RenameInstance(KSPListScreen.InstallName(manager, ksp), name.Value); } }
/// <summary> /// Return whether the fields are valid. /// Similar to adding, except leaving the fields unchanged is allowed. /// </summary> protected override bool Valid() { if (name.Value != KSPListScreen.InstallName(manager, ksp) && !nameValid()) { return(false); } if (path.Value != ksp.GameDir() && !pathValid()) { return(false); } return(true); }
/// <summary> /// Show the splash screen and wait for a key press. /// </summary> public bool Run() { // If there's a default instance, try to get the lock for it. if (manager.GetPreferredInstance() != null && !KSPListScreen.TryGetInstance(manager.CurrentInstance, () => Draw(false))) { Console.ResetColor(); Console.Clear(); Console.CursorVisible = true; return(false); } // Draw screen with press any key Draw(true); // Wait for a key Console.ReadKey(true); return(true); }
/// <summary> /// Initialize the Screen /// </summary> /// <param name="mgr">KSP manager containing the instances</param> /// <param name="k">Instance to edit</param> public KSPEditScreen(KSPManager mgr, KSP k) : base(mgr, KSPListScreen.InstallName(mgr, k), k.GameDir()) { ksp = k; try { // If we can't parse the registry, just leave the repo list blank registry = RegistryManager.Instance(ksp).registry; } catch { } // Show the repositories if we can if (registry != null) { // Need to edit a copy of the list so it doesn't save on cancel repoEditList = new SortedDictionary <string, Repository>(); foreach (var kvp in registry.Repositories) { repoEditList.Add(kvp.Key, new Repository( kvp.Value.name, kvp.Value.uri.ToString(), kvp.Value.priority )); } // Also edit copy of the compatible versions compatEditList = new List <KspVersion>(ksp.GetCompatibleVersions()); // I'm not a huge fan of this layout, but I think it's better than just a label AddObject(new ConsoleDoubleFrame( 1, repoFrameTop, -1, compatFrameBottom, compatFrameTop, () => $"Mod List Sources", () => $"Additional Compatible Versions", () => ConsoleTheme.Current.LabelFg )); repoList = new ConsoleListBox <Repository>( 3, repoListTop, -3, repoListBottom, new List <Repository>(repoEditList.Values), new List <ConsoleListBoxColumn <Repository> >() { new ConsoleListBoxColumn <Repository>() { Header = "Index", Renderer = r => r.priority.ToString(), Width = 7 }, new ConsoleListBoxColumn <Repository>() { Header = "Name", Renderer = r => r.name, Width = 16 }, new ConsoleListBoxColumn <Repository>() { Header = "URL", Renderer = r => r.uri.ToString(), Width = 50 } }, 1, 0, ListSortDirection.Ascending ); AddObject(repoList); repoList.AddTip("A", "Add"); repoList.AddBinding(Keys.A, (object sender) => { LaunchSubScreen(new RepoAddScreen(repoEditList)); repoList.SetData(new List <Repository>(repoEditList.Values)); return(true); }); repoList.AddTip("R", "Remove"); repoList.AddBinding(Keys.R, (object sender) => { int oldPrio = repoList.Selection.priority; repoEditList.Remove(repoList.Selection.name); // Reshuffle the priorities to fill foreach (Repository r in repoEditList.Values) { if (r.priority > oldPrio) { --r.priority; } } repoList.SetData(new List <Repository>(repoEditList.Values)); return(true); }); repoList.AddTip("E", "Edit"); repoList.AddBinding(Keys.E, (object sender) => { LaunchSubScreen(new RepoEditScreen(repoEditList, repoList.Selection)); repoList.SetData(new List <Repository>(repoEditList.Values)); return(true); }); repoList.AddTip("-", "Up"); repoList.AddBinding(Keys.Minus, (object sender) => { if (repoList.Selection.priority > 0) { Repository prev = SortedDictFind(repoEditList, r => r.priority == repoList.Selection.priority - 1); if (prev != null) { ++prev.priority; } --repoList.Selection.priority; repoList.SetData(new List <Repository>(repoEditList.Values)); } return(true); }); repoList.AddTip("+", "Down"); repoList.AddBinding(Keys.Plus, (object sender) => { Repository next = SortedDictFind(repoEditList, r => r.priority == repoList.Selection.priority + 1); if (next != null) { --next.priority; } ++repoList.Selection.priority; repoList.SetData(new List <Repository>(repoEditList.Values)); return(true); }); compatList = new ConsoleListBox <KspVersion>( 3, compatListTop, -3, compatListBottom, compatEditList, new List <ConsoleListBoxColumn <KspVersion> >() { new ConsoleListBoxColumn <KspVersion>() { Header = "Version", Width = 10, Renderer = v => v.ToString(), Comparer = (a, b) => a.CompareTo(b) } }, 0, 0, ListSortDirection.Descending ); AddObject(compatList); compatList.AddTip("A", "Add"); compatList.AddBinding(Keys.A, (object sender) => { CompatibleVersionDialog vd = new CompatibleVersionDialog(); KspVersion newVersion = vd.Run(); DrawBackground(); if (newVersion != null && !compatEditList.Contains(newVersion)) { compatEditList.Add(newVersion); compatList.SetData(compatEditList); } return(true); }); compatList.AddTip("R", "Remove", () => compatList.Selection != null); compatList.AddBinding(Keys.R, (object sender) => { compatEditList.Remove(compatList.Selection); compatList.SetData(compatEditList); return(true); }); } else { // Notify the user that the registry doesn't parse AddObject(new ConsoleLabel( 1, repoFrameTop, -1, () => $"Failed to extract mod list sources from {KSPListScreen.InstallName(manager, ksp)}." )); } }
/// <summary> /// Initialize the screen /// </summary> /// <param name="mgr">KSP manager object containing the current instance</param> /// <param name="dbg">True if debug options should be available, false otherwise</param> public ModListScreen(KSPManager mgr, bool dbg) { debug = dbg; manager = mgr; registry = RegistryManager.Instance(manager.CurrentInstance).registry; moduleList = new ConsoleListBox <CkanModule>( 1, 4, -1, -2, GetAllMods(), new List <ConsoleListBoxColumn <CkanModule> >() { new ConsoleListBoxColumn <CkanModule>() { Header = "", Width = 1, Renderer = StatusSymbol }, new ConsoleListBoxColumn <CkanModule>() { Header = "Name", Width = 44, Renderer = m => m.name ?? "" }, new ConsoleListBoxColumn <CkanModule>() { Header = "Version", Width = 10, Renderer = m => Formatting.StripEpoch(m.version?.ToString() ?? ""), Comparer = (a, b) => a.version.CompareTo(b.version) }, new ConsoleListBoxColumn <CkanModule>() { Header = "Max KSP version", Width = 17, Renderer = m => registry.LatestCompatibleKSP(m.identifier)?.ToString() ?? "", Comparer = (a, b) => registry.LatestCompatibleKSP(a.identifier).CompareTo(registry.LatestCompatibleKSP(b.identifier)) } }, 1, 0, ListSortDirection.Descending, (CkanModule m, string filter) => { if (filter.StartsWith("@")) { string authorFilt = filter.Substring(1); if (string.IsNullOrEmpty(authorFilt)) { return(true); } else if (m.author != null) { foreach (string auth in m.author) { if (auth.IndexOf(authorFilt, StringComparison.CurrentCultureIgnoreCase) == 0) { return(true); } } } return(false); } else if (filter.StartsWith("~")) { if (filter.Length <= 1) { // Don't blank the list for just "~" by itself return(true); } else { switch (filter.Substring(1, 1)) { case "i": return(registry.IsInstalled(m.identifier, false)); case "u": return(registry.HasUpdate(m.identifier, manager.CurrentInstance.VersionCriteria())); case "n": // Filter new return(recent.Contains(m.identifier)); case "c": if (m.conflicts != null) { string conflictsWith = filter.Substring(2); // Search for mods depending on a given mod foreach (var rel in m.conflicts) { if (rel.name.IndexOf(conflictsWith, StringComparison.CurrentCultureIgnoreCase) == 0) { return(true); } } } return(false); case "d": if (m.depends != null) { string dependsOn = filter.Substring(2); // Search for mods depending on a given mod foreach (var rel in m.depends) { if (rel.name.IndexOf(dependsOn, StringComparison.CurrentCultureIgnoreCase) == 0) { return(true); } } } return(false); } } return(false); } else { return(m.identifier.IndexOf(filter, StringComparison.CurrentCultureIgnoreCase) >= 0 || m.name.IndexOf(filter, StringComparison.CurrentCultureIgnoreCase) >= 0 || [email protected](filter, StringComparison.CurrentCultureIgnoreCase) >= 0); } } ); searchBox = new ConsoleField(-searchWidth, 2, -1) { GhostText = () => Focused() == searchBox ? "<Type to search>" : "<Ctrl+F to search>" }; searchBox.OnChange += (ConsoleField sender, string newValue) => { moduleList.FilterString = newValue; }; AddObject(new ConsoleLabel( 1, 2, -searchWidth - 2, () => $"{moduleList.VisibleRowCount()} mods" )); AddObject(searchBox); AddObject(moduleList); AddBinding(Keys.CtrlQ, (object sender) => false); AddBinding(Keys.AltX, (object sender) => false); AddBinding(Keys.F1, (object sender) => Help()); AddBinding(Keys.AltH, (object sender) => Help()); AddBinding(Keys.F5, (object sender) => UpdateRegistry()); AddBinding(Keys.CtrlR, (object sender) => UpdateRegistry()); AddBinding(Keys.CtrlU, (object sender) => UpgradeAll()); // Now a bunch of convenience shortcuts so you don't get stuck in the search box searchBox.AddBinding(Keys.PageUp, (object sender) => { SetFocus(moduleList); return(true); }); searchBox.AddBinding(Keys.PageDown, (object sender) => { SetFocus(moduleList); return(true); }); searchBox.AddBinding(Keys.Enter, (object sender) => { SetFocus(moduleList); return(true); }); moduleList.AddBinding(Keys.CtrlF, (object sender) => { SetFocus(searchBox); return(true); }); moduleList.AddBinding(Keys.Escape, (object sender) => { searchBox.Clear(); return(true); }); moduleList.AddTip("Enter", "Details", () => moduleList.Selection != null ); moduleList.AddBinding(Keys.Enter, (object sender) => { if (moduleList.Selection != null) { LaunchSubScreen(new ModInfoScreen(manager, plan, moduleList.Selection, debug)); } return(true); }); // Conditionally show only one of these based on selected mod status moduleList.AddTip("+", "Install", () => moduleList.Selection != null && !registry.IsInstalled(moduleList.Selection.identifier, false) ); moduleList.AddTip("+", "Upgrade", () => moduleList.Selection != null && registry.HasUpdate(moduleList.Selection.identifier, manager.CurrentInstance.VersionCriteria()) ); moduleList.AddBinding(Keys.Plus, (object sender) => { if (moduleList.Selection != null) { if (!registry.IsInstalled(moduleList.Selection.identifier, false)) { plan.ToggleInstall(moduleList.Selection.identifier); } else if (registry.IsInstalled(moduleList.Selection.identifier, false) && registry.HasUpdate(moduleList.Selection.identifier, manager.CurrentInstance.VersionCriteria())) { plan.ToggleUpgrade(moduleList.Selection.identifier); } } return(true); }); moduleList.AddTip("-", "Remove", () => moduleList.Selection != null && (registry.IsInstalled(moduleList.Selection.identifier, false)) ); moduleList.AddBinding(Keys.Minus, (object sender) => { if (moduleList.Selection != null && registry.IsInstalled(moduleList.Selection.identifier, false)) { plan.ToggleRemove(moduleList.Selection.identifier); } return(true); }); AddTip("F9", "Apply changes", plan.NonEmpty); AddBinding(Keys.F9, (object sender) => { ApplyChanges(); return(true); }); // Show total download size of all installed mods AddObject(new ConsoleLabel( 1, -1, searchWidth, () => $"{Formatting.FmtSize(totalInstalledDownloadSize())} installed", null, () => ConsoleTheme.Current.DimLabelFg )); AddObject(new ConsoleLabel( -searchWidth, -1, -2, () => { int days = daysSinceUpdated(registryFilePath()); return(days < 1 ? "" : days == 1 ? $"Updated at least {days} day ago" : $"Updated at least {days} days ago"); }, null, () => { int daysSince = daysSinceUpdated(registryFilePath()); if (daysSince < daysTillStale) { return(ConsoleTheme.Current.RegistryUpToDate); } else if (daysSince < daystillVeryStale) { return(ConsoleTheme.Current.RegistryStale); } else { return(ConsoleTheme.Current.RegistryVeryStale); } } )); List <ConsoleMenuOption> opts = new List <ConsoleMenuOption>() { new ConsoleMenuOption("Sort...", "", "Change the sorting of the list of mods", true, null, null, moduleList.SortMenu()), null, new ConsoleMenuOption("Refresh mod list", "F5, Ctrl+R", "Refresh the list of mods", true, UpdateRegistry), new ConsoleMenuOption("Upgrade all", "Ctrl+U", "Mark all available updates for installation", true, UpgradeAll), new ConsoleMenuOption("Audit recommendations", "", "List mods suggested and recommended by installed mods", true, ViewSuggestions), new ConsoleMenuOption("Scan KSP dir", "", "Check for manually installed mods", true, ScanForMods), new ConsoleMenuOption("Import downloads...", "", "Select manually downloaded mods to import into CKAN", true, ImportDownloads), new ConsoleMenuOption("Export installed...", "", "Save your mod list", true, ExportInstalled), null, new ConsoleMenuOption("Select KSP install...", "", "Switch to a different game instance", true, SelectInstall), null, new ConsoleMenuOption("Help", helpKey, "Tips & tricks", true, Help), null, new ConsoleMenuOption("Quit", "Ctrl+Q", "Exit to DOS", true, () => false) }; if (debug) { opts.Add(null); opts.Add(new ConsoleMenuOption("DEBUG: Capture key...", "", "Print details of how your system reports a keystroke for debugging", true, CaptureKey)); } mainMenu = new ConsolePopupMenu(opts); LeftHeader = () => $"CKAN {Meta.GetVersion()}"; CenterHeader = () => $"KSP {manager.CurrentInstance.Version().ToString()} ({KSPListScreen.InstallName(manager, manager.CurrentInstance)})"; }