diff --git a/.editorconfig b/.editorconfig
index b7f5dce684..da701fb2dd 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -42,9 +42,18 @@ dotnet_diagnostic.IDE0055.severity = none
# OK to call functions that return values and not use them
dotnet_diagnostic.IDE0058.severity = none
+# I like seeing when a using block ends
+dotnet_diagnostic.IDE0063.severity = none
+
# A `using` inside a namespace is useful as a typedef
dotnet_diagnostic.IDE0065.severity = none
+# 'switch' expressions are nice, but I don't need to be alerted about them
+dotnet_diagnostic.IDE0066.severity = none
+
+# Why do 'new' expressions need to be simple?
+dotnet_diagnostic.IDE0090.severity = none
+
# Allow namespaces to be independent of folder names
dotnet_diagnostic.IDE0130.severity = none
diff --git a/AutoUpdate/CKAN-autoupdate.csproj b/AutoUpdate/CKAN-autoupdate.csproj
index 581f14cd2b..b0c4aaec2f 100644
--- a/AutoUpdate/CKAN-autoupdate.csproj
+++ b/AutoUpdate/CKAN-autoupdate.csproj
@@ -16,10 +16,12 @@
true
Debug;Release
false
- 7
+ 9
+ enable
+ true
..\assets\ckan.ico
- net48;net7.0-windows
- true
+ net48;net8.0-windows
+ true
true
512
prompt
diff --git a/AutoUpdate/Main.cs b/AutoUpdate/Main.cs
index df9d915005..4d384dc32f 100644
--- a/AutoUpdate/Main.cs
+++ b/AutoUpdate/Main.cs
@@ -4,6 +4,9 @@
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
+#if NET5_0_OR_GREATER
+using System.Runtime.Versioning;
+#endif
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* CKAN AUTO-UPDATE TOOL
@@ -145,7 +148,7 @@ private static void MakeExecutable(string path)
{
UseShellExecute = false
});
- permsprocess.WaitForExit();
+ permsprocess?.WaitForExit();
}
}
@@ -158,8 +161,17 @@ private static bool IsOnMono
///
/// Are we on Windows?
///
- private static bool IsOnWindows
- => RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+ #if NET6_0_OR_GREATER
+ [SupportedOSPlatformGuard("windows")]
+ #endif
+ private static readonly bool IsOnWindows
+ = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+
+ #if NET8_0_OR_GREATER
+ [SupportedOSPlatformGuard("windows6.1")]
+ private static readonly bool IsOnWindows61
+ = IsOnWindows && OperatingSystem.IsWindowsVersionAtLeast(6, 1);
+ #endif
///
/// Display unexpected exceptions to user
@@ -179,11 +191,20 @@ private static void ReportError(string message, params object[] args)
{
string err = string.Format(message, args);
Console.Error.WriteLine(err);
- if (fromGui)
+ #if NETFRAMEWORK || WINDOWS
+ if (
+ #if NET8_0_OR_GREATER
+ IsOnWindows61 &&
+ #endif
+ fromGui)
{
// Show a popup in case the console isn't open
- MessageBox.Show(err, Properties.Resources.FatalErrorTitle, MessageBoxButtons.OK, MessageBoxIcon.Error);
+ MessageBox.Show(err,
+ Properties.Resources.FatalErrorTitle,
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error);
}
+ #endif
}
private const int maxRetries = 8;
diff --git a/AutoUpdate/SingleAssemblyResourceManager.cs b/AutoUpdate/SingleAssemblyResourceManager.cs
index 3ee0990a59..b6cf720637 100644
--- a/AutoUpdate/SingleAssemblyResourceManager.cs
+++ b/AutoUpdate/SingleAssemblyResourceManager.cs
@@ -1,4 +1,3 @@
-using System.IO;
using System.Globalization;
using System.Resources;
using System.Reflection;
@@ -14,16 +13,13 @@ public SingleAssemblyResourceManager(string basename, Assembly assembly) : base(
{
}
- protected override ResourceSet InternalGetResourceSet(CultureInfo culture,
+ protected override ResourceSet? InternalGetResourceSet(CultureInfo culture,
bool createIfNotExists, bool tryParents)
{
- if (!myResourceSets.TryGetValue(culture, out ResourceSet rs) && createIfNotExists)
+ if (!myResourceSets.TryGetValue(culture, out ResourceSet? rs) && createIfNotExists && MainAssembly != null)
{
// Lazy-load default language (without caring about duplicate assignment in race conditions, no harm done)
- if (neutralResourcesCulture == null)
- {
- neutralResourcesCulture = GetNeutralResourcesLanguage(MainAssembly);
- }
+ neutralResourcesCulture ??= GetNeutralResourcesLanguage(MainAssembly);
// If we're asking for the default language, then ask for the
// invariant (non-specific) resources.
@@ -33,7 +29,7 @@ protected override ResourceSet InternalGetResourceSet(CultureInfo culture,
}
string resourceFileName = GetResourceFileName(culture);
- Stream store = MainAssembly.GetManifestResourceStream(resourceFileName);
+ var store = MainAssembly.GetManifestResourceStream(resourceFileName);
// If we found the appropriate resources in the local assembly
if (store != null)
@@ -50,7 +46,7 @@ protected override ResourceSet InternalGetResourceSet(CultureInfo culture,
return rs;
}
- private CultureInfo neutralResourcesCulture;
+ private CultureInfo? neutralResourcesCulture;
private readonly Dictionary myResourceSets = new Dictionary();
}
}
diff --git a/Cmdline/Action/AuthToken.cs b/Cmdline/Action/AuthToken.cs
index 3850a2923b..03edb02694 100644
--- a/Cmdline/Action/AuthToken.cs
+++ b/Cmdline/Action/AuthToken.cs
@@ -26,7 +26,9 @@ public AuthToken() { }
///
/// Exit code
///
- public int RunSubCommand(GameInstanceManager manager, CommonOptions opts, SubCommandOptions unparsed)
+ public int RunSubCommand(GameInstanceManager? manager,
+ CommonOptions? opts,
+ SubCommandOptions unparsed)
{
string[] args = unparsed.options.ToArray();
int exitCode = Exit.OK;
@@ -37,12 +39,9 @@ public int RunSubCommand(GameInstanceManager manager, CommonOptions opts, SubCom
{
CommonOptions options = (CommonOptions)suboptions;
options.Merge(opts);
- user = new ConsoleUser(options.Headless);
- if (manager == null)
- {
- manager = new GameInstanceManager(user);
- }
- exitCode = options.Handle(manager, user);
+ user = new ConsoleUser(options.Headless);
+ manager ??= new GameInstanceManager(user);
+ exitCode = options.Handle(manager, user);
if (exitCode == Exit.OK)
{
switch (option)
@@ -75,7 +74,7 @@ private int listAuthTokens()
foreach (string host in hosts)
{
longestHostLen = Math.Max(longestHostLen, host.Length);
- if (ServiceLocator.Container.Resolve().TryGetAuthToken(host, out string token))
+ if (ServiceLocator.Container.Resolve().TryGetAuthToken(host, out string? token))
{
longestTokenLen = Math.Max(longestTokenLen, token.Length);
}
@@ -83,16 +82,15 @@ private int listAuthTokens()
// Create format string: {0,-longestHostLen} {1,-longestTokenLen}
string fmt = string.Format("{0}0,-{2}{1} {0}1,-{3}{1}",
"{", "}", longestHostLen, longestTokenLen);
- user.RaiseMessage(fmt, hostHeader, tokenHeader);
- user.RaiseMessage(fmt,
- new string('-', longestHostLen),
- new string('-', longestTokenLen)
- );
+ user?.RaiseMessage(fmt, hostHeader, tokenHeader);
+ user?.RaiseMessage(fmt,
+ new string('-', longestHostLen),
+ new string('-', longestTokenLen));
foreach (string host in hosts)
{
- if (ServiceLocator.Container.Resolve().TryGetAuthToken(host, out string token))
+ if (ServiceLocator.Container.Resolve().TryGetAuthToken(host, out string? token))
{
- user.RaiseMessage(fmt, host, token);
+ user?.RaiseMessage(fmt, host, token);
}
}
}
@@ -101,36 +99,42 @@ private int listAuthTokens()
private int addAuthToken(AddAuthTokenOptions opts)
{
- if (Uri.CheckHostName(opts.host) != UriHostNameType.Unknown)
- {
- ServiceLocator.Container.Resolve().SetAuthToken(opts.host, opts.token);
- }
- else
+ if (opts.host is string h)
{
- user.RaiseError(Properties.Resources.AuthTokenInvalidHostName, opts.host);
+ if (Uri.CheckHostName(h) != UriHostNameType.Unknown)
+ {
+ ServiceLocator.Container.Resolve().SetAuthToken(h, opts.token);
+ }
+ else
+ {
+ user?.RaiseError(Properties.Resources.AuthTokenInvalidHostName, h);
+ }
}
return Exit.OK;
}
private int removeAuthToken(RemoveAuthTokenOptions opts)
{
- ServiceLocator.Container.Resolve().SetAuthToken(opts.host, null);
+ if (opts.host is string h)
+ {
+ ServiceLocator.Container.Resolve().SetAuthToken(h, null);
+ }
return Exit.OK;
}
- private IUser user;
+ private IUser? user;
}
internal class AuthTokenSubOptions : VerbCommandOptions
{
[VerbOption("list", HelpText = "List auth tokens")]
- public CommonOptions ListOptions { get; set; }
+ public CommonOptions? ListOptions { get; set; }
[VerbOption("add", HelpText = "Add an auth token")]
- public AddAuthTokenOptions AddOptions { get; set; }
+ public AddAuthTokenOptions? AddOptions { get; set; }
[VerbOption("remove", HelpText = "Delete an auth token")]
- public RemoveAuthTokenOptions RemoveOptions { get; set; }
+ public RemoveAuthTokenOptions? RemoveOptions { get; set; }
[HelpVerbOption]
public string GetUsage(string verb)
@@ -173,13 +177,13 @@ public static IEnumerable GetHelp(string verb)
internal class AddAuthTokenOptions : CommonOptions
{
- [ValueOption(0)] public string host { get; set; }
- [ValueOption(1)] public string token { get; set; }
+ [ValueOption(0)] public string? host { get; set; }
+ [ValueOption(1)] public string? token { get; set; }
}
internal class RemoveAuthTokenOptions : CommonOptions
{
- [ValueOption(0)] public string host { get; set; }
+ [ValueOption(0)] public string? host { get; set; }
}
}
diff --git a/Cmdline/Action/Cache.cs b/Cmdline/Action/Cache.cs
index a8c4faaed5..2db6343dcb 100644
--- a/Cmdline/Action/Cache.cs
+++ b/Cmdline/Action/Cache.cs
@@ -11,22 +11,22 @@ namespace CKAN.CmdLine
public class CacheSubOptions : VerbCommandOptions
{
[VerbOption("list", HelpText = "List the download cache path")]
- public CommonOptions ListOptions { get; set; }
+ public CommonOptions? ListOptions { get; set; }
[VerbOption("set", HelpText = "Set the download cache path")]
- public SetOptions SetOptions { get; set; }
+ public SetOptions? SetOptions { get; set; }
[VerbOption("clear", HelpText = "Clear the download cache directory")]
- public CommonOptions ClearOptions { get; set; }
+ public CommonOptions? ClearOptions { get; set; }
[VerbOption("reset", HelpText = "Set the download cache path to the default")]
- public CommonOptions ResetOptions { get; set; }
+ public CommonOptions? ResetOptions { get; set; }
[VerbOption("showlimit", HelpText = "Show the cache size limit")]
- public CommonOptions ShowLimitOptions { get; set; }
+ public CommonOptions? ShowLimitOptions { get; set; }
[VerbOption("setlimit", HelpText = "Set the cache size limit")]
- public SetLimitOptions SetLimitOptions { get; set; }
+ public SetLimitOptions? SetLimitOptions { get; set; }
[HelpVerbOption]
public string GetUsage(string verb)
@@ -77,7 +77,7 @@ public static IEnumerable GetHelp(string verb)
public class SetOptions : CommonOptions
{
[ValueOption(0)]
- public string Path { get; set; }
+ public string? Path { get; set; }
}
public class SetLimitOptions : CommonOptions
@@ -99,7 +99,9 @@ public Cache() { }
///
/// Exit code for shell environment
///
- public int RunSubCommand(GameInstanceManager mgr, CommonOptions opts, SubCommandOptions unparsed)
+ public int RunSubCommand(GameInstanceManager? mgr,
+ CommonOptions? opts,
+ SubCommandOptions unparsed)
{
string[] args = unparsed.options.ToArray();
@@ -159,7 +161,7 @@ public int RunSubCommand(GameInstanceManager mgr, CommonOptions opts, SubCommand
private int ListCacheDirectory()
{
IConfiguration cfg = ServiceLocator.Container.Resolve();
- user.RaiseMessage(cfg.DownloadCacheDir);
+ user?.RaiseMessage("{0}", cfg.DownloadCacheDir ?? "");
printCacheInfo();
return Exit.OK;
}
@@ -168,44 +170,51 @@ private int SetCacheDirectory(SetOptions options)
{
if (string.IsNullOrEmpty(options.Path))
{
- user.RaiseError(Properties.Resources.ArgumentMissing);
+ user?.RaiseError(Properties.Resources.ArgumentMissing);
PrintUsage("set");
return Exit.BADOPT;
}
- if (manager.TrySetupCache(options.Path, out string failReason))
+ if (manager != null)
{
- IConfiguration cfg = ServiceLocator.Container.Resolve();
- user.RaiseMessage(Properties.Resources.CacheSet, cfg.DownloadCacheDir);
- printCacheInfo();
- return Exit.OK;
- }
- else
- {
- user.RaiseError(Properties.Resources.CacheInvalidPath, failReason);
- return Exit.BADOPT;
+ if (manager.TrySetupCache(options.Path, out string? failReason))
+ {
+ IConfiguration cfg = ServiceLocator.Container.Resolve();
+ user?.RaiseMessage(Properties.Resources.CacheSet, cfg.DownloadCacheDir ?? "");
+ printCacheInfo();
+ return Exit.OK;
+ }
+ else
+ {
+ user?.RaiseError(Properties.Resources.CacheInvalidPath, failReason);
+ return Exit.BADOPT;
+ }
}
+ return Exit.ERROR;
}
private int ClearCacheDirectory()
{
- manager.Cache.RemoveAll();
- user.RaiseMessage(Properties.Resources.CacheCleared);
+ manager?.Cache?.RemoveAll();
+ user?.RaiseMessage(Properties.Resources.CacheCleared);
printCacheInfo();
return Exit.OK;
}
private int ResetCacheDirectory()
{
- if (manager.TrySetupCache("", out string failReason))
+ if (manager != null)
{
- IConfiguration cfg = ServiceLocator.Container.Resolve();
- user.RaiseMessage(Properties.Resources.CacheReset, cfg.DownloadCacheDir);
- printCacheInfo();
- }
- else
- {
- user.RaiseError(Properties.Resources.CacheResetFailed, failReason);
+ if (manager.TrySetupCache("", out string? failReason))
+ {
+ IConfiguration cfg = ServiceLocator.Container.Resolve();
+ user?.RaiseMessage(Properties.Resources.CacheReset, cfg.DownloadCacheDir ?? "");
+ printCacheInfo();
+ }
+ else
+ {
+ user?.RaiseError(Properties.Resources.CacheResetFailed, failReason);
+ }
}
return Exit.OK;
}
@@ -215,11 +224,11 @@ private int ShowCacheSizeLimit()
IConfiguration cfg = ServiceLocator.Container.Resolve();
if (cfg.CacheSizeLimit.HasValue)
{
- user.RaiseMessage(CkanModule.FmtSize(cfg.CacheSizeLimit.Value));
+ user?.RaiseMessage(CkanModule.FmtSize(cfg.CacheSizeLimit.Value));
}
else
{
- user.RaiseMessage(Properties.Resources.CacheUnlimited);
+ user?.RaiseMessage(Properties.Resources.CacheUnlimited);
}
return Exit.OK;
}
@@ -228,30 +237,33 @@ private int SetCacheSizeLimit(SetLimitOptions options)
{
IConfiguration cfg = ServiceLocator.Container.Resolve();
cfg.CacheSizeLimit = options.Megabytes < 0
- ? null :
- (long?)(options.Megabytes * 1024 * 1024);
+ ? null
+ : (options.Megabytes * 1024 * 1024);
return ShowCacheSizeLimit();
}
private void printCacheInfo()
{
- manager.Cache.GetSizeInfo(out int fileCount, out long bytes, out long bytesFree);
- user.RaiseMessage(Properties.Resources.CacheInfo,
- fileCount,
- CkanModule.FmtSize(bytes),
- CkanModule.FmtSize(bytesFree));
+ if (manager?.Cache != null)
+ {
+ manager.Cache.GetSizeInfo(out int fileCount, out long bytes, out long bytesFree);
+ user?.RaiseMessage(Properties.Resources.CacheInfo,
+ fileCount,
+ CkanModule.FmtSize(bytes),
+ CkanModule.FmtSize(bytesFree));
+ }
}
private void PrintUsage(string verb)
{
foreach (var h in CacheSubOptions.GetHelp(verb))
{
- user.RaiseError(h);
+ user?.RaiseError(h);
}
}
- private IUser user;
- private GameInstanceManager manager;
+ private IUser? user;
+ private GameInstanceManager? manager;
}
}
diff --git a/Cmdline/Action/Compare.cs b/Cmdline/Action/Compare.cs
index 85d6c028b3..495b010693 100644
--- a/Cmdline/Action/Compare.cs
+++ b/Cmdline/Action/Compare.cs
@@ -65,8 +65,8 @@ internal class CompareOptions : CommonOptions
[Option("machine-readable", HelpText = "Output in a machine readable format: -1, 0 or 1")]
public bool machine_readable { get; set;}
- [ValueOption(0)] public string Left { get; set; }
- [ValueOption(1)] public string Right { get; set; }
+ [ValueOption(0)] public string? Left { get; set; }
+ [ValueOption(1)] public string? Right { get; set; }
}
}
diff --git a/Cmdline/Action/Compat.cs b/Cmdline/Action/Compat.cs
index c6380e97e9..a89673f140 100644
--- a/Cmdline/Action/Compat.cs
+++ b/Cmdline/Action/Compat.cs
@@ -12,19 +12,19 @@ namespace CKAN.CmdLine
public class CompatSubOptions : VerbCommandOptions
{
[VerbOption("list", HelpText = "List compatible game versions")]
- public CompatListOptions List { get; set; }
+ public CompatListOptions? List { get; set; }
[VerbOption("clear", HelpText = "Forget all compatible game versions")]
- public CompatClearOptions Clear { get; set; }
+ public CompatClearOptions? Clear { get; set; }
[VerbOption("add", HelpText = "Add versions to compatible game versions list")]
- public CompatAddOptions Add { get; set; }
+ public CompatAddOptions? Add { get; set; }
[VerbOption("forget", HelpText = "Forget compatible game versions")]
- public CompatForgetOptions Forget { get; set; }
+ public CompatForgetOptions? Forget { get; set; }
[VerbOption("set", HelpText = "Set the compatible game versions list")]
- public CompatSetOptions Set { get; set; }
+ public CompatSetOptions? Set { get; set; }
[HelpVerbOption]
public string GetUsage(string verb)
@@ -76,26 +76,26 @@ public class CompatClearOptions : InstanceSpecificOptions { }
public class CompatAddOptions : InstanceSpecificOptions
{
[ValueList(typeof(List))]
- public List Versions { get; set; }
+ public List? Versions { get; set; }
}
public class CompatForgetOptions : InstanceSpecificOptions
{
[ValueList(typeof(List))]
- public List Versions { get; set; }
+ public List? Versions { get; set; }
}
public class CompatSetOptions : InstanceSpecificOptions
{
[ValueList(typeof(List))]
- public List Versions { get; set; }
+ public List? Versions { get; set; }
}
public class Compat : ISubCommand
{
- public int RunSubCommand(GameInstanceManager mgr,
- CommonOptions opts,
- SubCommandOptions options)
+ public int RunSubCommand(GameInstanceManager? mgr,
+ CommonOptions? opts,
+ SubCommandOptions options)
{
var exitCode = Exit.OK;
@@ -129,21 +129,21 @@ public int RunSubCommand(GameInstanceManager mgr,
break;
case "add":
- exitCode = Add(suboptions as CompatAddOptions,
+ exitCode = Add((CompatAddOptions)suboptions,
MainClass.GetGameInstance(manager))
? Exit.OK
: Exit.ERROR;
break;
case "forget":
- exitCode = Forget(suboptions as CompatForgetOptions,
+ exitCode = Forget((CompatForgetOptions)suboptions,
MainClass.GetGameInstance(manager))
? Exit.OK
: Exit.ERROR;
break;
case "set":
- exitCode = Set(suboptions as CompatSetOptions,
+ exitCode = Set((CompatSetOptions)suboptions,
MainClass.GetGameInstance(manager))
? Exit.OK
: Exit.ERROR;
@@ -162,46 +162,42 @@ private bool List(CKAN.GameInstance instance)
{
var versionHeader = Properties.Resources.CompatVersionHeader;
var actualHeader = Properties.Resources.CompatActualHeader;
- var output = Enumerable.Repeat(new
- {
- Version = instance.Version(),
- Actual = true,
- },
+ var output = (instance.Version() is GameVersion v
+ ? Enumerable.Repeat((Version: v,
+ Actual: true),
1)
+ : Enumerable.Empty<(GameVersion Version, bool Actual)>())
.Concat(instance.GetCompatibleVersions()
.OrderByDescending(v => v)
- .Select(v => new
- {
- Version = v,
- Actual = false,
- }))
+ .Select(v => (Version: v,
+ Actual: false)))
.ToList();
var versionWidth = Enumerable.Repeat(versionHeader, 1)
.Concat(output.Select(i => i.Version.ToString()))
- .Max(i => i.Length);
+ .Max(i => i?.Length ?? 0);
var actualWidth = Enumerable.Repeat(actualHeader, 1)
.Concat(output.Select(i => i.Actual.ToString()))
- .Max(i => i.Length);
+ .Max(i => i?.Length ?? 0);
const string columnFormat = "{0} {1}";
- user.RaiseMessage(columnFormat,
- versionHeader.PadRight(versionWidth),
- actualHeader.PadRight(actualWidth));
+ user?.RaiseMessage(columnFormat,
+ versionHeader.PadRight(versionWidth),
+ actualHeader.PadRight(actualWidth));
- user.RaiseMessage(columnFormat,
- new string('-', versionWidth),
- new string('-', actualWidth));
+ user?.RaiseMessage(columnFormat,
+ new string('-', versionWidth),
+ new string('-', actualWidth));
foreach (var line in output)
{
- user.RaiseMessage(columnFormat,
- line.Version.ToString()
- .PadRight(versionWidth),
- line.Actual.ToString()
- .PadRight(actualWidth));
+ user?.RaiseMessage(columnFormat,
+ (line.Version.ToString() ?? "")
+ .PadRight(versionWidth),
+ line.Actual.ToString()
+ .PadRight(actualWidth));
}
return true;
}
@@ -216,9 +212,9 @@ private bool Clear(CKAN.GameInstance instance)
private bool Add(CompatAddOptions addOptions,
CKAN.GameInstance instance)
{
- if (addOptions.Versions.Count < 1)
+ if (addOptions.Versions?.Count < 1)
{
- user.RaiseError(Properties.Resources.CompatMissing);
+ user?.RaiseError(Properties.Resources.CompatMissing);
PrintUsage("add");
return false;
}
@@ -226,7 +222,7 @@ private bool Add(CompatAddOptions addOptions,
out GameVersion[] goodVers,
out string[] badVers))
{
- user.RaiseError(Properties.Resources.CompatInvalid,
+ user?.RaiseError(Properties.Resources.CompatInvalid,
string.Join(", ", badVers));
return false;
}
@@ -241,9 +237,9 @@ private bool Add(CompatAddOptions addOptions,
private bool Forget(CompatForgetOptions forgetOptions,
CKAN.GameInstance instance)
{
- if (forgetOptions.Versions.Count < 1)
+ if (forgetOptions.Versions?.Count < 1)
{
- user.RaiseError(Properties.Resources.CompatMissing);
+ user?.RaiseError(Properties.Resources.CompatMissing);
PrintUsage("forget");
return false;
}
@@ -251,18 +247,19 @@ private bool Forget(CompatForgetOptions forgetOptions,
out GameVersion[] goodVers,
out string[] badVers))
{
- user.RaiseError(Properties.Resources.CompatInvalid,
- string.Join(", ", badVers));
+ user?.RaiseError(Properties.Resources.CompatInvalid,
+ string.Join(", ", badVers));
return false;
}
- var rmActualVers = goodVers.Intersect(new GameVersion[] { instance.Version(),
- instance.Version().WithoutBuild })
+ var rmActualVers = goodVers.Intersect(instance.Version() is GameVersion gv
+ ? new GameVersion[] { gv, gv.WithoutBuild }
+ : Array.Empty())
.Select(gv => gv.ToString())
.ToArray();
if (rmActualVers.Length > 0)
{
- user.RaiseError(Properties.Resources.CompatCantForget,
- string.Join(", ", rmActualVers));
+ user?.RaiseError(Properties.Resources.CompatCantForget,
+ string.Join(", ", rmActualVers));
return false;
}
instance.SetCompatibleVersions(instance.GetCompatibleVersions()
@@ -275,9 +272,9 @@ private bool Forget(CompatForgetOptions forgetOptions,
private bool Set(CompatSetOptions setOptions,
CKAN.GameInstance instance)
{
- if (setOptions.Versions.Count < 1)
+ if (setOptions.Versions?.Count < 1)
{
- user.RaiseError(Properties.Resources.CompatMissing);
+ user?.RaiseError(Properties.Resources.CompatMissing);
PrintUsage("set");
return false;
}
@@ -285,8 +282,8 @@ private bool Set(CompatSetOptions setOptions,
out GameVersion[] goodVers,
out string[] badVers))
{
- user.RaiseError(Properties.Resources.CompatInvalid,
- string.Join(", ", badVers));
+ user?.RaiseError(Properties.Resources.CompatInvalid,
+ string.Join(", ", badVers));
return false;
}
instance.SetCompatibleVersions(goodVers.Distinct().ToList());
@@ -298,18 +295,18 @@ private void PrintUsage(string verb)
{
foreach (var h in CompatSubOptions.GetHelp(verb))
{
- user.RaiseError(h);
+ user?.RaiseError(h);
}
}
- private static bool TryParseVersions(IEnumerable versions,
- out GameVersion[] goodVers,
- out string[] badVers)
+ private static bool TryParseVersions(IEnumerable? versions,
+ out GameVersion[] goodVers,
+ out string[] badVers)
{
- var gameVersions = versions
- .Select(v => GameVersion.TryParse(v, out GameVersion gv)
- ? new Tuple(v, gv)
- : new Tuple(v, null))
+ var gameVersions = (versions ?? Enumerable.Empty())
+ .Select(v => GameVersion.TryParse(v, out GameVersion? gv)
+ ? new Tuple(v, gv)
+ : new Tuple(v, null))
.ToArray();
goodVers = gameVersions.Select(tuple => tuple.Item2)
.OfType()
@@ -320,7 +317,7 @@ private static bool TryParseVersions(IEnumerable versions,
return badVers.Length < 1;
}
- private IUser user;
- private GameInstanceManager manager;
+ private IUser? user;
+ private GameInstanceManager? manager;
}
}
diff --git a/Cmdline/Action/Filter.cs b/Cmdline/Action/Filter.cs
index 253edd115f..565700ecd6 100644
--- a/Cmdline/Action/Filter.cs
+++ b/Cmdline/Action/Filter.cs
@@ -28,7 +28,9 @@ public Filter() { }
///
/// Exit code
///
- public int RunSubCommand(GameInstanceManager mgr, CommonOptions opts, SubCommandOptions unparsed)
+ public int RunSubCommand(GameInstanceManager? mgr,
+ CommonOptions? opts,
+ SubCommandOptions unparsed)
{
string[] args = unparsed.options.ToArray();
int exitCode = Exit.OK;
@@ -74,39 +76,43 @@ public int RunSubCommand(GameInstanceManager mgr, CommonOptions opts, SubCommand
private int ListFilters(FilterListOptions opts)
{
- int exitCode = opts.Handle(manager, user);
+ int exitCode = manager != null && user != null
+ ? opts.Handle(manager, user)
+ : Exit.ERROR;
if (exitCode != Exit.OK)
{
return exitCode;
}
var cfg = ServiceLocator.Container.Resolve();
- user.RaiseMessage(Properties.Resources.FilterListGlobalHeader);
+ user?.RaiseMessage(Properties.Resources.FilterListGlobalHeader);
foreach (string filter in cfg.GlobalInstallFilters)
{
- user.RaiseMessage("\t- {0}", filter);
+ user?.RaiseMessage("\t- {0}", filter);
}
- user.RaiseMessage("");
+ user?.RaiseMessage("");
var instance = MainClass.GetGameInstance(manager);
- user.RaiseMessage(Properties.Resources.FilterListInstanceHeader);
+ user?.RaiseMessage(Properties.Resources.FilterListInstanceHeader);
foreach (string filter in instance.InstallFilters)
{
- user.RaiseMessage("\t- {0}", filter);
+ user?.RaiseMessage("\t- {0}", filter);
}
return Exit.OK;
}
private int AddFilters(FilterAddOptions opts, string verb)
{
- if (opts.filters.Count < 1)
+ if (opts.filters?.Count < 1)
{
- user.RaiseError(Properties.Resources.ArgumentMissing);
+ user?.RaiseError(Properties.Resources.ArgumentMissing);
PrintUsage(verb);
return Exit.BADOPT;
}
- int exitCode = opts.Handle(manager, user);
+ int exitCode = manager != null && user != null
+ ? opts.Handle(manager, user)
+ : Exit.ERROR;
if (exitCode != Exit.OK)
{
return exitCode;
@@ -116,20 +122,18 @@ private int AddFilters(FilterAddOptions opts, string verb)
{
var cfg = ServiceLocator.Container.Resolve();
var duplicates = cfg.GlobalInstallFilters
- .Intersect(opts.filters)
+ .Intersect(opts.filters ?? new List { })
.ToArray();
if (duplicates.Length > 0)
{
- user.RaiseError(
- Properties.Resources.FilterAddGlobalDuplicateError,
- string.Join(", ", duplicates)
- );
+ user?.RaiseError(Properties.Resources.FilterAddGlobalDuplicateError,
+ string.Join(", ", duplicates));
return Exit.BADOPT;
}
else
{
cfg.GlobalInstallFilters = cfg.GlobalInstallFilters
- .Concat(opts.filters)
+ .Concat(opts.filters ?? new List { })
.Distinct()
.ToArray();
}
@@ -138,20 +142,18 @@ private int AddFilters(FilterAddOptions opts, string verb)
{
var instance = MainClass.GetGameInstance(manager);
var duplicates = instance.InstallFilters
- .Intersect(opts.filters)
+ .Intersect(opts.filters ?? new List { })
.ToArray();
if (duplicates.Length > 0)
{
- user.RaiseError(
- Properties.Resources.FilterAddInstanceDuplicateError,
- string.Join(", ", duplicates)
- );
+ user?.RaiseError(Properties.Resources.FilterAddInstanceDuplicateError,
+ string.Join(", ", duplicates));
return Exit.BADOPT;
}
else
{
instance.InstallFilters = instance.InstallFilters
- .Concat(opts.filters)
+ .Concat(opts.filters ?? new List { })
.Distinct()
.ToArray();
}
@@ -161,14 +163,16 @@ private int AddFilters(FilterAddOptions opts, string verb)
private int RemoveFilters(FilterRemoveOptions opts, string verb)
{
- if (opts.filters.Count < 1)
+ if (opts.filters?.Count < 1)
{
- user.RaiseError(Properties.Resources.ArgumentMissing);
+ user?.RaiseError(Properties.Resources.ArgumentMissing);
PrintUsage(verb);
return Exit.BADOPT;
}
- int exitCode = opts.Handle(manager, user);
+ int exitCode = manager != null && user != null
+ ? opts.Handle(manager, user)
+ : Exit.ERROR;
if (exitCode != Exit.OK)
{
return exitCode;
@@ -177,43 +181,40 @@ private int RemoveFilters(FilterRemoveOptions opts, string verb)
if (opts.global)
{
var cfg = ServiceLocator.Container.Resolve();
- var notFound = opts.filters
+ var notFound = (opts.filters ?? new List { })
.Except(cfg.GlobalInstallFilters)
.ToArray();
if (notFound.Length > 0)
{
- user.RaiseError(
+ user?.RaiseError(
Properties.Resources.FilterRemoveGlobalNotFoundError,
- string.Join(", ", notFound)
- );
+ string.Join(", ", notFound));
return Exit.BADOPT;
}
else
{
cfg.GlobalInstallFilters = cfg.GlobalInstallFilters
- .Except(opts.filters)
+ .Except(opts.filters ?? new List { })
.ToArray();
}
}
else
{
var instance = MainClass.GetGameInstance(manager);
- var notFound = opts.filters
+ var notFound = (opts.filters ?? new List { })
.Except(instance.InstallFilters)
.ToArray();
if (notFound.Length > 0)
{
- user.RaiseError(
- Properties.Resources.FilterRemoveInstanceNotFoundError,
- string.Join(", ", notFound)
- );
+ user?.RaiseError(Properties.Resources.FilterRemoveInstanceNotFoundError,
+ string.Join(", ", notFound));
return Exit.BADOPT;
}
else
{
instance.InstallFilters = instance.InstallFilters
- .Except(opts.filters)
- .ToArray();
+ .Except(opts.filters ?? new List { })
+ .ToArray();
}
}
return Exit.OK;
@@ -223,24 +224,24 @@ private void PrintUsage(string verb)
{
foreach (var h in FilterSubOptions.GetHelp(verb))
{
- user.RaiseError(h);
+ user?.RaiseError(h);
}
}
- private GameInstanceManager manager;
- private IUser user;
+ private GameInstanceManager? manager;
+ private IUser? user;
}
internal class FilterSubOptions : VerbCommandOptions
{
[VerbOption("list", HelpText = "List install filters")]
- public FilterListOptions FilterListOptions { get; set; }
+ public FilterListOptions? FilterListOptions { get; set; }
[VerbOption("add", HelpText = "Add install filters")]
- public FilterAddOptions FilterAddOptions { get; set; }
+ public FilterAddOptions? FilterAddOptions { get; set; }
[VerbOption("remove", HelpText = "Remove install filters")]
- public FilterRemoveOptions FilterRemoveOptions { get; set; }
+ public FilterRemoveOptions? FilterRemoveOptions { get; set; }
[HelpVerbOption]
public string GetUsage(string verb)
@@ -293,7 +294,7 @@ internal class FilterAddOptions : InstanceSpecificOptions
public bool global { get; set; }
[ValueList(typeof(List))]
- public List filters { get; set; }
+ public List? filters { get; set; }
}
internal class FilterRemoveOptions : InstanceSpecificOptions
@@ -302,7 +303,7 @@ internal class FilterRemoveOptions : InstanceSpecificOptions
public bool global { get; set; }
[ValueList(typeof(List))]
- public List filters { get; set; }
+ public List? filters { get; set; }
}
}
diff --git a/Cmdline/Action/GameInstance.cs b/Cmdline/Action/GameInstance.cs
index 335d909a98..c2945afe84 100644
--- a/Cmdline/Action/GameInstance.cs
+++ b/Cmdline/Action/GameInstance.cs
@@ -16,25 +16,25 @@ namespace CKAN.CmdLine
internal class InstanceSubOptions : VerbCommandOptions
{
[VerbOption("list", HelpText = "List game instances")]
- public CommonOptions ListOptions { get; set; }
+ public CommonOptions? ListOptions { get; set; }
[VerbOption("add", HelpText = "Add a game instance")]
- public AddOptions AddOptions { get; set; }
+ public AddOptions? AddOptions { get; set; }
[VerbOption("clone", HelpText = "Clone an existing game instance")]
- public CloneOptions CloneOptions { get; set; }
+ public CloneOptions? CloneOptions { get; set; }
[VerbOption("rename", HelpText = "Rename a game instance")]
- public RenameOptions RenameOptions { get; set; }
+ public RenameOptions? RenameOptions { get; set; }
[VerbOption("forget", HelpText = "Forget a game instance")]
- public ForgetOptions ForgetOptions { get; set; }
+ public ForgetOptions? ForgetOptions { get; set; }
[VerbOption("default", HelpText = "Set the default game instance")]
- public DefaultOptions DefaultOptions { get; set; }
+ public DefaultOptions? DefaultOptions { get; set; }
[VerbOption("fake", HelpText = "Fake a game instance")]
- public FakeOptions FakeOptions { get; set; }
+ public FakeOptions? FakeOptions { get; set; }
[HelpVerbOption]
public string GetUsage(string verb)
@@ -99,8 +99,8 @@ public static IEnumerable GetHelp(string verb)
internal class AddOptions : CommonOptions
{
- [ValueOption(0)] public string name { get; set; }
- [ValueOption(1)] public string path { get; set; }
+ [ValueOption(0)] public string? name { get; set; }
+ [ValueOption(1)] public string? path { get; set; }
}
internal class CloneOptions : CommonOptions
@@ -108,43 +108,43 @@ internal class CloneOptions : CommonOptions
[Option("share-stock", DefaultValue = false, HelpText = "Use junction points (Windows) or symbolic links (Unix) for stock dirs instead of copying")]
public bool shareStock { get; set; }
- [ValueOption(0)] public string nameOrPath { get; set; }
- [ValueOption(1)] public string new_name { get; set; }
- [ValueOption(2)] public string new_path { get; set; }
+ [ValueOption(0)] public string? nameOrPath { get; set; }
+ [ValueOption(1)] public string? new_name { get; set; }
+ [ValueOption(2)] public string? new_path { get; set; }
}
internal class RenameOptions : CommonOptions
{
[GameInstances]
- [ValueOption(0)] public string old_name { get; set; }
- [ValueOption(1)] public string new_name { get; set; }
+ [ValueOption(0)] public string? old_name { get; set; }
+ [ValueOption(1)] public string? new_name { get; set; }
}
internal class ForgetOptions : CommonOptions
{
[GameInstances]
- [ValueOption(0)] public string name { get; set; }
+ [ValueOption(0)] public string? name { get; set; }
}
internal class DefaultOptions : CommonOptions
{
[GameInstances]
- [ValueOption(0)] public string name { get; set; }
+ [ValueOption(0)] public string? name { get; set; }
}
internal class FakeOptions : CommonOptions
{
- [ValueOption(0)] public string name { get; set; }
- [ValueOption(1)] public string path { get; set; }
- [ValueOption(2)] public string version { get; set; }
+ [ValueOption(0)] public string? name { get; set; }
+ [ValueOption(1)] public string? path { get; set; }
+ [ValueOption(2)] public string? version { get; set; }
[Option("game", DefaultValue = "KSP", HelpText = "The game of the instance to be faked, either KSP or KSP2")]
- public string gameId { get; set; }
+ public string? gameId { get; set; }
[Option("MakingHistory", DefaultValue = "none", HelpText = "The version of the Making History DLC to be faked.")]
- public string makingHistoryVersion { get; set; }
+ public string? makingHistoryVersion { get; set; }
[Option("BreakingGround", DefaultValue = "none", HelpText = "The version of the Breaking Ground DLC to be faked.")]
- public string breakingGroundVersion { get; set; }
+ public string? breakingGroundVersion { get; set; }
[Option("set-default", DefaultValue = false, HelpText = "Set the new instance as the default one.")]
public bool setDefault { get; set; }
@@ -156,7 +156,9 @@ public GameInstance() { }
protected static readonly ILog log = LogManager.GetLogger(typeof(GameInstance));
// This is required by ISubCommand
- public int RunSubCommand(GameInstanceManager manager, CommonOptions opts, SubCommandOptions unparsed)
+ public int RunSubCommand(GameInstanceManager? manager,
+ CommonOptions? opts,
+ SubCommandOptions unparsed)
{
string[] args = unparsed.options.ToArray();
@@ -235,14 +237,14 @@ public int RunSubCommand(GameInstanceManager manager, CommonOptions opts, SubCom
return exitCode;
}
- private IUser user { get; set; }
- private GameInstanceManager Manager { get; set; }
+ private IUser? user { get; set; }
+ private GameInstanceManager? Manager { get; set; }
#region option functions
private int ListInstalls()
{
- var output = Manager.Instances
+ var output = Manager?.Instances
.OrderByDescending(i => i.Value.Name == Manager.AutoStartInstance)
.ThenByDescending(i => i.Value.Version() ?? GameVersion.Any)
.ThenBy(i => i.Key)
@@ -257,70 +259,74 @@ private int ListInstalls()
})
.ToList();
- string nameHeader = Properties.Resources.InstanceListNameHeader;
- string versionHeader = Properties.Resources.InstanceListVersionHeader;
- string defaultHeader = Properties.Resources.InstanceListDefaultHeader;
- string pathHeader = Properties.Resources.InstanceListPathHeader;
+ if (output != null)
+ {
+ string nameHeader = Properties.Resources.InstanceListNameHeader;
+ string versionHeader = Properties.Resources.InstanceListVersionHeader;
+ string defaultHeader = Properties.Resources.InstanceListDefaultHeader;
+ string pathHeader = Properties.Resources.InstanceListPathHeader;
- var nameWidth = Enumerable.Repeat(nameHeader, 1).Concat(output.Select(i => i.Name)).Max(i => i.Length);
- var versionWidth = Enumerable.Repeat(versionHeader, 1).Concat(output.Select(i => i.Version)).Max(i => i.Length);
- var defaultWidth = Enumerable.Repeat(defaultHeader, 1).Concat(output.Select(i => i.Default)).Max(i => i.Length);
- var pathWidth = Enumerable.Repeat(pathHeader, 1).Concat(output.Select(i => i.Path)).Max(i => i.Length);
+ var nameWidth = Enumerable.Repeat(nameHeader, 1).Concat(output.Select(i => i.Name)).Max(i => i.Length);
+ var versionWidth = Enumerable.Repeat(versionHeader, 1).Concat(output.Select(i => i.Version)).Max(i => i.Length);
+ var defaultWidth = Enumerable.Repeat(defaultHeader, 1).Concat(output.Select(i => i.Default)).Max(i => i.Length);
+ var pathWidth = Enumerable.Repeat(pathHeader, 1).Concat(output.Select(i => i.Path)).Max(i => i.Length);
- const string columnFormat = "{0} {1} {2} {3}";
+ const string columnFormat = "{0} {1} {2} {3}";
- user.RaiseMessage(columnFormat,
- nameHeader.PadRight(nameWidth),
- versionHeader.PadRight(versionWidth),
- defaultHeader.PadRight(defaultWidth),
- pathHeader.PadRight(pathWidth)
- );
+ user?.RaiseMessage(columnFormat,
+ nameHeader.PadRight(nameWidth),
+ versionHeader.PadRight(versionWidth),
+ defaultHeader.PadRight(defaultWidth),
+ pathHeader.PadRight(pathWidth));
- user.RaiseMessage(columnFormat,
- new string('-', nameWidth),
- new string('-', versionWidth),
- new string('-', defaultWidth),
- new string('-', pathWidth)
- );
+ user?.RaiseMessage(columnFormat,
+ new string('-', nameWidth),
+ new string('-', versionWidth),
+ new string('-', defaultWidth),
+ new string('-', pathWidth));
- foreach (var line in output)
- {
- user.RaiseMessage(columnFormat,
- line.Name.PadRight(nameWidth),
- line.Version.PadRight(versionWidth),
- line.Default.PadRight(defaultWidth),
- line.Path.PadRight(pathWidth)
- );
- }
+ foreach (var line in output)
+ {
+ user?.RaiseMessage(columnFormat,
+ line.Name.PadRight(nameWidth),
+ line.Version.PadRight(versionWidth),
+ line.Default.PadRight(defaultWidth),
+ line.Path.PadRight(pathWidth));
+ }
- return Exit.OK;
+ return Exit.OK;
+ }
+ return Exit.ERROR;
}
private int AddInstall(AddOptions options)
{
if (options.name == null || options.path == null)
{
- user.RaiseError(Properties.Resources.ArgumentMissing);
+ user?.RaiseError(Properties.Resources.ArgumentMissing);
PrintUsage("add");
return Exit.BADOPT;
}
- if (Manager.HasInstance(options.name))
+ if (Manager?.HasInstance(options.name) ?? false)
{
- user.RaiseMessage(Properties.Resources.InstanceAddDuplicate, options.name);
+ user?.RaiseMessage(Properties.Resources.InstanceAddDuplicate, options.name);
return Exit.BADOPT;
}
try
{
- string path = options.path;
- Manager.AddInstance(path, options.name, user);
- user.RaiseMessage(Properties.Resources.InstanceAdded, options.name, options.path);
+ if (user != null)
+ {
+ string path = options.path;
+ Manager?.AddInstance(path, options.name, user);
+ user.RaiseMessage(Properties.Resources.InstanceAdded, options.name, options.path);
+ }
return Exit.OK;
}
catch (NotKSPDirKraken ex)
{
- user.RaiseMessage(Properties.Resources.InstanceNotInstance, ex.path);
+ user?.RaiseMessage(Properties.Resources.InstanceNotInstance, ex.path);
return Exit.BADOPT;
}
}
@@ -329,7 +335,7 @@ private int CloneInstall(CloneOptions options)
{
if (options.nameOrPath == null || options.new_name == null || options.new_path == null)
{
- user.RaiseError(Properties.Resources.ArgumentMissing);
+ user?.RaiseError(Properties.Resources.ArgumentMissing);
PrintUsage("clone");
return Exit.BADOPT;
}
@@ -345,7 +351,7 @@ private int CloneInstall(CloneOptions options)
try
{
// Try instanceNameOrPath as name and search the registry for it.
- if (Manager.HasInstance(instanceNameOrPath))
+ if (Manager?.HasInstance(instanceNameOrPath) ?? false)
{
CKAN.GameInstance[] listOfInstances = Manager.Instances.Values.ToArray();
foreach (CKAN.GameInstance instance in listOfInstances)
@@ -360,7 +366,7 @@ private int CloneInstall(CloneOptions options)
}
// Try to use instanceNameOrPath as a path and create a new game instance.
// If it's valid, go on.
- else if (Manager.InstanceAt(instanceNameOrPath) is CKAN.GameInstance instance && instance.Valid)
+ else if (Manager?.InstanceAt(instanceNameOrPath) is CKAN.GameInstance instance && instance.Valid)
{
Manager.CloneInstance(instance, newName, newPath, options.shareStock);
}
@@ -398,13 +404,13 @@ private int CloneInstall(CloneOptions options)
}
catch (NoGameInstanceKraken)
{
- user.RaiseError(Properties.Resources.InstanceCloneNotFound, instanceNameOrPath);
+ user?.RaiseError(Properties.Resources.InstanceCloneNotFound, instanceNameOrPath);
ListInstalls();
return Exit.ERROR;
}
catch (InstanceNameTakenKraken kraken)
{
- user.RaiseError(Properties.Resources.InstanceDuplicate, kraken.instName);
+ user?.RaiseError(Properties.Resources.InstanceDuplicate, kraken.instName);
return Exit.BADOPT;
}
@@ -417,7 +423,7 @@ private int CloneInstall(CloneOptions options)
}
else
{
- user.RaiseMessage(Properties.Resources.InstanceCloneFailed);
+ user?.RaiseMessage(Properties.Resources.InstanceCloneFailed);
return Exit.ERROR;
}
}
@@ -426,20 +432,20 @@ private int RenameInstall(RenameOptions options)
{
if (options.old_name == null || options.new_name == null)
{
- user.RaiseError(Properties.Resources.ArgumentMissing);
+ user?.RaiseError(Properties.Resources.ArgumentMissing);
PrintUsage("rename");
return Exit.BADOPT;
}
- if (!Manager.HasInstance(options.old_name))
+ if (!Manager?.HasInstance(options.old_name) ?? false)
{
- user.RaiseMessage(Properties.Resources.InstanceNotFound, options.old_name);
+ user?.RaiseMessage(Properties.Resources.InstanceNotFound, options.old_name);
return Exit.BADOPT;
}
- Manager.RenameInstance(options.old_name, options.new_name);
+ Manager?.RenameInstance(options.old_name, options.new_name);
- user.RaiseMessage(Properties.Resources.InstanceRenamed, options.old_name, options.new_name);
+ user?.RaiseMessage(Properties.Resources.InstanceRenamed, options.old_name, options.new_name);
return Exit.OK;
}
@@ -447,94 +453,77 @@ private int ForgetInstall(ForgetOptions options)
{
if (options.name == null)
{
- user.RaiseError(Properties.Resources.ArgumentMissing);
+ user?.RaiseError(Properties.Resources.ArgumentMissing);
PrintUsage("forget");
return Exit.BADOPT;
}
- if (!Manager.HasInstance(options.name))
+ if (!Manager?.HasInstance(options.name) ?? false)
{
- user.RaiseMessage(Properties.Resources.InstanceNotFound, options.name);
+ user?.RaiseMessage(Properties.Resources.InstanceNotFound, options.name);
return Exit.BADOPT;
}
- Manager.RemoveInstance(options.name);
+ Manager?.RemoveInstance(options.name);
- user.RaiseMessage(Properties.Resources.InstanceForgot, options.name);
+ user?.RaiseMessage(Properties.Resources.InstanceForgot, options.name);
return Exit.OK;
}
private int SetDefaultInstall(DefaultOptions options)
{
- string name = options.name;
-
- if (name == null)
+ var name = options.name;
+ // Oh right, this is that one that didn't work AT ALL at one point
+ if (name == null && Manager != null && user != null)
{
// No input argument from the user. Present a list of the possible instances.
- string message = $"default - {Properties.Resources.InstanceDefaultArgumentMissing}";
-
- // Check if there is a default instance.
- string defaultInstance = Manager.Configuration.AutoStartInstance;
- int defaultInstancePresent = 0;
- if (!string.IsNullOrWhiteSpace(defaultInstance))
+ try
{
- defaultInstancePresent = 1;
+ // If there is a default instance, its index is passed first
+ var defaultInstance = Manager.Configuration.AutoStartInstance;
+ int result = user.RaiseSelectionDialog(
+ $"default - {Properties.Resources.InstanceDefaultArgumentMissing}",
+ (defaultInstance == null || string.IsNullOrWhiteSpace(defaultInstance)
+ ? Enumerable.Empty
- public ConsoleCKAN(GameInstanceManager mgr, string themeName, bool debug)
+ public ConsoleCKAN(GameInstanceManager? mgr, string? themeName, bool debug)
{
- if (ConsoleTheme.Themes.TryGetValue(themeName ?? "default", out ConsoleTheme theme))
+ if (ConsoleTheme.Themes.TryGetValue(themeName ?? "default", out ConsoleTheme? theme))
{
var repoData = ServiceLocator.Container.Resolve();
// GameInstanceManager only uses its IUser object to construct game instance objects,
@@ -35,19 +35,19 @@ public ConsoleCKAN(GameInstanceManager mgr, string themeName, bool debug)
if (manager.CurrentInstance == null) {
if (manager.Instances.Count == 0) {
// No instances, add one
- new GameInstanceAddScreen(manager).Run(theme);
+ new GameInstanceAddScreen(theme, manager).Run();
// Set instance to current if they added one
manager.GetPreferredInstance();
} else {
// Multiple instances, no default, pick one
- new GameInstanceListScreen(manager, repoData).Run(theme);
+ new GameInstanceListScreen(theme, manager, repoData).Run();
}
}
if (manager.CurrentInstance != null) {
- new ModListScreen(manager, repoData,
+ new ModListScreen(theme, manager, repoData,
RegistryManager.Instance(manager.CurrentInstance, repoData),
manager.CurrentInstance.game,
- debug, theme).Run(theme);
+ debug).Run();
}
new ExitScreen().Run(theme);
diff --git a/ConsoleUI/DeleteDirectoriesScreen.cs b/ConsoleUI/DeleteDirectoriesScreen.cs
index 057291b147..3084005cf6 100644
--- a/ConsoleUI/DeleteDirectoriesScreen.cs
+++ b/ConsoleUI/DeleteDirectoriesScreen.cs
@@ -14,10 +14,13 @@ public class DeleteDirectoriesScreen : ConsoleScreen {
///
/// Initialize the screen
///
+ /// The visual theme to use to draw the dialog
/// Game instance
/// Deletable stuff the user should see
- public DeleteDirectoriesScreen(GameInstance inst,
+ public DeleteDirectoriesScreen(ConsoleTheme theme,
+ GameInstance inst,
HashSet possibleConfigOnlyDirs)
+ : base(theme)
{
instance = inst;
toDelete = possibleConfigOnlyDirs.ToHashSet();
@@ -32,29 +35,37 @@ public DeleteDirectoriesScreen(GameInstance inst,
1, 6, listWidth - 1, -2,
possibleConfigOnlyDirs.OrderBy(d => d).ToList(),
new List>() {
- new ConsoleListBoxColumn() {
- Header = "",
- Width = 1,
- Renderer = s => toDelete.Contains(s) ? Symbols.checkmark : "",
- },
- new ConsoleListBoxColumn() {
- Header = Properties.Resources.DeleteDirectoriesFoldersHeader,
- Width = null,
+ new ConsoleListBoxColumn(
+ "",
+ s => toDelete.Contains(s) ? Symbols.checkmark : "",
+ null,
+ 1),
+ new ConsoleListBoxColumn(
+ Properties.Resources.DeleteDirectoriesFoldersHeader,
// The data model holds absolute paths, but the UI shows relative
- Renderer = p => Platform.FormatPath(instance.ToRelativeGameDir(p)),
- },
+ p => Platform.FormatPath(instance.ToRelativeGameDir(p)),
+ null,
+ null),
},
0, -1);
directories.AddTip("D", Properties.Resources.DeleteDirectoriesDeleteDirTip,
- () => !toDelete.Contains(directories.Selection));
- directories.AddBinding(Keys.D, (object sender, ConsoleTheme theme) => {
- toDelete.Add(directories.Selection);
+ () => directories.Selection is not null
+ && !toDelete.Contains(directories.Selection));
+ directories.AddBinding(Keys.D, (object sender) => {
+ if (directories.Selection is not null)
+ {
+ toDelete.Add(directories.Selection);
+ }
return true;
});
directories.AddTip("K", Properties.Resources.DeleteDirectoriesKeepDirTip,
- () => toDelete.Contains(directories.Selection));
- directories.AddBinding(Keys.K, (object sender, ConsoleTheme theme) => {
- toDelete.Remove(directories.Selection);
+ () => directories.Selection is not null
+ && toDelete.Contains(directories.Selection));
+ directories.AddBinding(Keys.K, (object sender) => {
+ if (directories.Selection is not null)
+ {
+ toDelete.Remove(directories.Selection);
+ }
return true;
});
directories.SelectionChanged += PopulateFiles;
@@ -64,11 +75,13 @@ public DeleteDirectoriesScreen(GameInstance inst,
listWidth + 1, 6, -1, -2,
new List() { "" },
new List>() {
- new ConsoleListBoxColumn() {
- Header = Properties.Resources.DeleteDirectoriesFilesHeader,
- Width = null,
- Renderer = p => Platform.FormatPath(CKANPathUtils.ToRelative(p, directories.Selection)),
- },
+ new ConsoleListBoxColumn(
+ Properties.Resources.DeleteDirectoriesFilesHeader,
+ p => directories.Selection is null
+ ? ""
+ : Platform.FormatPath(CKANPathUtils.ToRelative(p, directories.Selection)),
+ null,
+ null),
},
0, -1);
AddObject(files);
@@ -77,10 +90,10 @@ public DeleteDirectoriesScreen(GameInstance inst,
Properties.Resources.DeleteDirectoriesCancelTip);
AddBinding(Keys.Escape,
// Discard changes
- (object sender, ConsoleTheme theme) => false);
+ (object sender) => false);
AddTip("F9", Properties.Resources.DeleteDirectoriesApplyTip);
- AddBinding(Keys.F9, (object sender, ConsoleTheme theme) => {
+ AddBinding(Keys.F9, (object sender) => {
foreach (var d in toDelete) {
try {
Directory.Delete(d, true);
@@ -96,11 +109,13 @@ public DeleteDirectoriesScreen(GameInstance inst,
private void PopulateFiles()
{
files.SetData(
- Directory.EnumerateFileSystemEntries(directories.Selection,
- "*",
- SearchOption.AllDirectories)
- .OrderBy(f => f)
- .ToList(),
+ directories.Selection is null
+ ? new List()
+ : Directory.EnumerateFileSystemEntries(directories.Selection,
+ "*",
+ SearchOption.AllDirectories)
+ .OrderBy(f => f)
+ .ToList(),
true);
}
diff --git a/ConsoleUI/DependencyScreen.cs b/ConsoleUI/DependencyScreen.cs
index b20b53d364..44b8e8c29d 100644
--- a/ConsoleUI/DependencyScreen.cs
+++ b/ConsoleUI/DependencyScreen.cs
@@ -2,6 +2,7 @@
using System.Linq;
using System.ComponentModel;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using CKAN.Versioning;
#if NETFRAMEWORK
@@ -20,12 +21,13 @@ public class DependencyScreen : ConsoleScreen {
///
/// Initialize the screen
///
+ /// The visual theme to use to draw the dialog
/// Game instance manager containing instances
/// Registry of the current instance for finding mods
/// Plan of mods to add and remove
/// Mods that the user saw and did not select, in this pass or a previous pass
/// True if debug options should be available, false otherwise
- public DependencyScreen(GameInstanceManager mgr, Registry reg, ChangePlan cp, HashSet rej, bool dbg) : base()
+ public DependencyScreen(ConsoleTheme theme, GameInstanceManager mgr, Registry reg, ChangePlan cp, HashSet rej, bool dbg) : base(theme)
{
debug = dbg;
manager = mgr;
@@ -36,45 +38,48 @@ public DependencyScreen(GameInstanceManager mgr, Registry reg, ChangePlan cp, Ha
AddObject(new ConsoleLabel(1, 2, -1,
() => Properties.Resources.RecommendationsLabel));
- generateList(new ModuleInstaller(manager.CurrentInstance, manager.Cache, this),
- plan.Install
- .Concat(ReplacementModules(plan.Replace,
- manager.CurrentInstance.VersionCriteria()))
- .ToHashSet());
+ if (manager.CurrentInstance != null && manager.Cache != null)
+ {
+ generateList(new ModuleInstaller(manager.CurrentInstance, manager.Cache, this),
+ plan.Install
+ .Concat(ReplacementModules(plan.Replace,
+ manager.CurrentInstance.VersionCriteria()))
+ .ToHashSet());
+ }
dependencyList = new ConsoleListBox(
1, 4, -1, -2,
new List(dependencies.Values),
new List>() {
- new ConsoleListBoxColumn() {
- Header = Properties.Resources.RecommendationsInstallHeader,
- Width = 7,
- Renderer = (Dependency d) => StatusSymbol(d.module),
- },
- new ConsoleListBoxColumn() {
- Header = Properties.Resources.RecommendationsNameHeader,
- Width = null,
- Renderer = (Dependency d) => d.module.ToString(),
- },
- new ConsoleListBoxColumn() {
- Header = Properties.Resources.RecommendationsSourcesHeader,
- Width = 42,
- Renderer = (Dependency d) => string.Join(", ", d.dependents),
- }
+ new ConsoleListBoxColumn(
+ Properties.Resources.RecommendationsInstallHeader,
+ (Dependency d) => StatusSymbol(d.module),
+ null,
+ 7),
+ new ConsoleListBoxColumn(
+ Properties.Resources.RecommendationsNameHeader,
+ (Dependency d) => d.module.ToString(),
+ null,
+ null),
+ new ConsoleListBoxColumn(
+ Properties.Resources.RecommendationsSourcesHeader,
+ (Dependency d) => string.Join(", ", d.dependents),
+ null,
+ 42)
},
1, 0, ListSortDirection.Descending
);
dependencyList.AddTip("+", Properties.Resources.Toggle);
- dependencyList.AddBinding(Keys.Plus, (object sender, ConsoleTheme theme) => {
- var mod = dependencyList.Selection.module;
- if (accepted.Contains(mod) || TryWithoutConflicts(accepted.Append(mod))) {
+ dependencyList.AddBinding(Keys.Plus, (object sender) => {
+ if (dependencyList.Selection?.module is CkanModule mod
+ && (accepted.Contains(mod) || TryWithoutConflicts(accepted.Append(mod)))) {
ChangePlan.toggleContains(accepted, mod);
}
return true;
});
dependencyList.AddTip($"{Properties.Resources.Ctrl}+A", Properties.Resources.SelectAll);
- dependencyList.AddBinding(Keys.CtrlA, (object sender, ConsoleTheme theme) => {
+ dependencyList.AddBinding(Keys.CtrlA, (object sender) => {
if (TryWithoutConflicts(dependencies.Keys)) {
foreach (var kvp in dependencies) {
if (!accepted.Contains(kvp.Key)) {
@@ -86,15 +91,15 @@ public DependencyScreen(GameInstanceManager mgr, Registry reg, ChangePlan cp, Ha
});
dependencyList.AddTip($"{Properties.Resources.Ctrl}+D", Properties.Resources.DeselectAll, () => accepted.Count > 0);
- dependencyList.AddBinding(Keys.CtrlD, (object sender, ConsoleTheme theme) => {
+ dependencyList.AddBinding(Keys.CtrlD, (object sender) => {
accepted.Clear();
return true;
});
dependencyList.AddTip(Properties.Resources.Enter, Properties.Resources.Details);
- dependencyList.AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
+ dependencyList.AddBinding(Keys.Enter, (object sender) => {
if (dependencyList.Selection != null) {
- LaunchSubScreen(theme, new ModInfoScreen(manager, reg, plan,
+ LaunchSubScreen(new ModInfoScreen(theme, manager, reg, plan,
dependencyList.Selection.module,
debug));
}
@@ -104,14 +109,14 @@ public DependencyScreen(GameInstanceManager mgr, Registry reg, ChangePlan cp, Ha
AddObject(dependencyList);
AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
- AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
+ AddBinding(Keys.Escape, (object sender) => {
// Add everything to rejected
rejected.UnionWith(dependencies.Keys.Select(m => m.identifier));
return false;
});
AddTip("F9", Properties.Resources.Accept);
- AddBinding(Keys.F9, (object sender, ConsoleTheme theme) => {
+ AddBinding(Keys.F9, (object sender) => {
if (TryWithoutConflicts(accepted)) {
plan.Install.UnionWith(accepted);
// Add the rest to rejected
@@ -143,28 +148,25 @@ public DependencyScreen(GameInstanceManager mgr, Registry reg, ChangePlan cp, Ha
private void generateList(ModuleInstaller installer, HashSet inst)
{
if (installer.FindRecommendations(
- inst, new List(inst), registry as Registry,
+ inst, new List(inst), registry,
out Dictionary>> recommendations,
out Dictionary> suggestions,
out Dictionary> supporters
)) {
foreach ((CkanModule mod, Tuple> checkedAndDependents) in recommendations) {
- dependencies.Add(mod, new Dependency() {
- module = mod,
- dependents = checkedAndDependents.Item2.OrderBy(d => d).ToList()
- });
+ dependencies.Add(mod, new Dependency(
+ mod,
+ checkedAndDependents.Item2.OrderBy(d => d).ToList()));
}
foreach ((CkanModule mod, List dependents) in suggestions) {
- dependencies.Add(mod, new Dependency() {
- module = mod,
- dependents = dependents.OrderBy(d => d).ToList()
- });
+ dependencies.Add(mod, new Dependency(
+ mod,
+ dependents.OrderBy(d => d).ToList()));
}
foreach ((CkanModule mod, HashSet dependents) in supporters) {
- dependencies.Add(mod, new Dependency() {
- module = mod,
- dependents = dependents.OrderBy(d => d).ToList()
- });
+ dependencies.Add(mod, new Dependency(
+ mod,
+ dependents.OrderBy(d => d).ToList()));
}
// Check the default checkboxes
accepted.UnionWith(recommendations.Where(kvp => kvp.Value.Item1)
@@ -175,7 +177,7 @@ out Dictionary> supporters
private IEnumerable ReplacementModules(IEnumerable replaced_identifiers,
GameVersionCriteria crit)
=> replaced_identifiers.Select(replaced => registry.GetReplacement(replaced, crit))
- .Where(repl => repl != null)
+ .OfType()
.Select(repl => repl.ReplaceWith);
private string StatusSymbol(CkanModule mod)
@@ -184,7 +186,7 @@ private string StatusSymbol(CkanModule mod)
private bool TryWithoutConflicts(IEnumerable toAdd)
{
- if (HasConflicts(toAdd, out List conflictDescriptions)) {
+ if (HasConflicts(toAdd, out List? conflictDescriptions)) {
RaiseError("{0}", string.Join(Environment.NewLine,
conflictDescriptions));
return false;
@@ -192,30 +194,36 @@ private bool TryWithoutConflicts(IEnumerable toAdd)
return true;
}
- private bool HasConflicts(IEnumerable toAdd,
- out List descriptions)
+ private bool HasConflicts(IEnumerable toAdd,
+ [NotNullWhen(true)] out List? descriptions)
{
- try
+ if (manager.CurrentInstance != null)
{
- var resolver = new RelationshipResolver(
- plan.Install.Concat(toAdd).Distinct(),
- plan.Remove.Select(ident => registry.InstalledModule(ident)?.Module),
- RelationshipResolverOptions.ConflictsOpts(), registry,
- manager.CurrentInstance.VersionCriteria());
- descriptions = resolver.ConflictDescriptions.ToList();
- return descriptions.Count > 0;
- }
- catch (DependencyNotSatisfiedKraken k)
- {
- descriptions = new List() { k.Message };
- return true;
+ try
+ {
+ var resolver = new RelationshipResolver(
+ plan.Install.Concat(toAdd).Distinct(),
+ plan.Remove.Select(ident => registry.InstalledModule(ident)?.Module)
+ .OfType(),
+ RelationshipResolverOptions.ConflictsOpts(), registry,
+ manager.CurrentInstance.VersionCriteria());
+ descriptions = resolver.ConflictDescriptions.ToList();
+ return descriptions.Count > 0;
+ }
+ catch (DependencyNotSatisfiedKraken k)
+ {
+ descriptions = new List() { k.Message };
+ return true;
+ }
}
+ descriptions = null;
+ return false;
}
private readonly HashSet accepted = new HashSet();
private readonly HashSet rejected;
- private readonly IRegistryQuerier registry;
+ private readonly Registry registry;
private readonly GameInstanceManager manager;
private readonly ChangePlan plan;
private readonly bool debug;
@@ -232,6 +240,17 @@ private bool HasConflicts(IEnumerable toAdd,
///
public class Dependency {
+ ///
+ /// Initialize a dependency
+ ///
+ /// The mod
+ /// Mods that recommend or suggest m
+ public Dependency(CkanModule m, List d)
+ {
+ module = m;
+ dependents = d;
+ }
+
///
/// The mod
///
@@ -240,7 +259,7 @@ public class Dependency {
///
/// List of mods that recommended or suggested this mod
///
- public List dependents = new List();
+ public readonly List dependents;
}
}
diff --git a/ConsoleUI/DownloadImportDialog.cs b/ConsoleUI/DownloadImportDialog.cs
index 2e8cb9e0c4..87b9b5d49c 100644
--- a/ConsoleUI/DownloadImportDialog.cs
+++ b/ConsoleUI/DownloadImportDialog.cs
@@ -21,24 +21,26 @@ public static class DownloadImportDialog {
/// Change plan object for marking things to be installed
public static void ImportDownloads(ConsoleTheme theme, GameInstance gameInst, RepositoryDataManager repoData, NetModuleCache cache, ChangePlan cp)
{
- ConsoleFileMultiSelectDialog cfmsd = new ConsoleFileMultiSelectDialog(
+ var cfmsd = new ConsoleFileMultiSelectDialog(
+ theme,
Properties.Resources.ImportSelectTitle,
FindDownloadsPath(gameInst),
"*.zip",
Properties.Resources.ImportSelectHeader,
Properties.Resources.ImportSelectHeader
);
- HashSet files = cfmsd.Run(theme);
+ HashSet files = cfmsd.Run();
if (files.Count > 0) {
ProgressScreen ps = new ProgressScreen(
+ theme,
Properties.Resources.ImportProgressTitle,
Properties.Resources.ImportProgressMessage);
ModuleInstaller inst = new ModuleInstaller(gameInst, cache, ps);
- ps.Run(theme, (ConsoleTheme th) => inst.ImportFiles(files, ps,
+ ps.Run(() => inst.ImportFiles(files, ps,
(CkanModule mod) => cp.Install.Add(mod), RegistryManager.Instance(gameInst, repoData).registry));
// Don't let the installer re-use old screen references
- inst.User = null;
+ inst.User = new NullUser();
}
}
diff --git a/ConsoleUI/GameInstanceAddScreen.cs b/ConsoleUI/GameInstanceAddScreen.cs
index 3d2764f480..adea587095 100644
--- a/ConsoleUI/GameInstanceAddScreen.cs
+++ b/ConsoleUI/GameInstanceAddScreen.cs
@@ -13,8 +13,10 @@ public class GameInstanceAddScreen : GameInstanceScreen {
///
/// Initialize the Screen
///
+ /// The visual theme to use to draw the dialog
/// Game instance manager containing the instances
- public GameInstanceAddScreen(GameInstanceManager mgr) : base(mgr)
+ public GameInstanceAddScreen(ConsoleTheme theme, GameInstanceManager mgr)
+ : base(theme, mgr)
{
AddObject(new ConsoleLabel(
labelWidth, pathRow + 1, -1,
diff --git a/ConsoleUI/GameInstanceEditScreen.cs b/ConsoleUI/GameInstanceEditScreen.cs
index b229e2ea7d..f61ece4f92 100644
--- a/ConsoleUI/GameInstanceEditScreen.cs
+++ b/ConsoleUI/GameInstanceEditScreen.cs
@@ -15,11 +15,15 @@ public class GameInstanceEditScreen : GameInstanceScreen {
///
/// Initialize the Screen
///
+ /// The visual theme to use to draw the dialog
/// Game instance manager containing the instances
/// Repository data manager providing info from repos
/// Instance to edit
- public GameInstanceEditScreen(GameInstanceManager mgr, RepositoryDataManager repoData, GameInstance k)
- : base(mgr, k.Name, k.GameDir())
+ public GameInstanceEditScreen(ConsoleTheme theme,
+ GameInstanceManager mgr,
+ RepositoryDataManager repoData,
+ GameInstance k)
+ : base(theme, mgr, k.Name, k.GameDir())
{
ksp = k;
try {
@@ -56,70 +60,84 @@ public GameInstanceEditScreen(GameInstanceManager mgr, RepositoryDataManager rep
3, repoListTop, -3, repoListBottom,
new List(repoEditList.Values),
new List>() {
- new ConsoleListBoxColumn() {
- Header = Properties.Resources.InstanceEditRepoIndexHeader,
- Renderer = r => r.priority.ToString(),
- Width = 7
- }, new ConsoleListBoxColumn() {
- Header = Properties.Resources.InstanceEditRepoNameHeader,
- Renderer = r => r.name,
- Width = 16
- }, new ConsoleListBoxColumn() {
- Header = Properties.Resources.InstanceEditRepoURLHeader,
- Renderer = r => r.uri.ToString(),
- Width = null
- }
+ new ConsoleListBoxColumn(
+ Properties.Resources.InstanceEditRepoIndexHeader,
+ r => r.priority.ToString(),
+ null,
+ 7),
+ new ConsoleListBoxColumn(
+ Properties.Resources.InstanceEditRepoNameHeader,
+ r => r.name,
+ null,
+ 16),
+ new ConsoleListBoxColumn(
+ Properties.Resources.InstanceEditRepoURLHeader,
+ r => r.uri.ToString(),
+ null,
+ null)
},
1, 0, ListSortDirection.Ascending
);
AddObject(repoList);
repoList.AddTip("A", Properties.Resources.Add);
- repoList.AddBinding(Keys.A, (object sender, ConsoleTheme theme) => {
- LaunchSubScreen(theme, new RepoAddScreen(ksp.game, repoEditList));
+ repoList.AddBinding(Keys.A, (object sender) => {
+ LaunchSubScreen(new RepoAddScreen(theme, ksp.game, repoEditList));
repoList.SetData(new List(repoEditList.Values));
return true;
});
repoList.AddTip("R", Properties.Resources.Remove);
- repoList.AddBinding(Keys.R, (object sender, ConsoleTheme theme) => {
- 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.AddBinding(Keys.R, (object sender) => {
+ if (repoList.Selection is Repository repo)
+ {
+ int oldPrio = repo.priority;
+ repoEditList.Remove(repo.name);
+ // Reshuffle the priorities to fill
+ foreach (Repository r in repoEditList.Values) {
+ if (r.priority > oldPrio) {
+ --r.priority;
+ }
}
+ repoList.SetData(new List(repoEditList.Values));
}
- repoList.SetData(new List(repoEditList.Values));
return true;
});
repoList.AddTip("E", Properties.Resources.Edit);
- repoList.AddBinding(Keys.E, (object sender, ConsoleTheme theme) => {
- LaunchSubScreen(theme, new RepoEditScreen(ksp.game, repoEditList, repoList.Selection));
- repoList.SetData(new List(repoEditList.Values));
+ repoList.AddBinding(Keys.E, (object sender) => {
+ if (repoList.Selection is Repository repo)
+ {
+ LaunchSubScreen(new RepoEditScreen(theme, ksp.game, repoEditList, repo));
+ repoList.SetData(new List(repoEditList.Values));
+ }
return true;
});
repoList.AddTip("-", Properties.Resources.Up);
- repoList.AddBinding(Keys.Minus, (object sender, ConsoleTheme theme) => {
- if (repoList.Selection.priority > 0) {
- Repository prev = SortedDictFind(repoEditList,
- r => r.priority == repoList.Selection.priority - 1);
- if (prev != null) {
- ++prev.priority;
+ repoList.AddBinding(Keys.Minus, (object sender) => {
+ if (repoList.Selection is Repository repo)
+ {
+ if (repo.priority > 0) {
+ var prev = SortedDictFind(repoEditList,
+ r => r.priority == repo.priority - 1);
+ if (prev != null) {
+ ++prev.priority;
+ }
+ --repo.priority;
+ repoList.SetData(new List(repoEditList.Values));
}
- --repoList.Selection.priority;
- repoList.SetData(new List(repoEditList.Values));
}
return true;
});
repoList.AddTip("+", Properties.Resources.Down);
- repoList.AddBinding(Keys.Plus, (object sender, ConsoleTheme theme) => {
- Repository next = SortedDictFind(repoEditList,
- r => r.priority == repoList.Selection.priority + 1);
- if (next != null) {
- --next.priority;
+ repoList.AddBinding(Keys.Plus, (object sender) => {
+ if (repoList.Selection is Repository repo)
+ {
+ var next = SortedDictFind(repoEditList,
+ r => r.priority == repo.priority + 1);
+ if (next != null) {
+ --next.priority;
+ }
+ ++repo.priority;
+ repoList.SetData(new List(repoEditList.Values));
}
- ++repoList.Selection.priority;
- repoList.SetData(new List(repoEditList.Values));
return true;
});
@@ -127,22 +145,21 @@ public GameInstanceEditScreen(GameInstanceManager mgr, RepositoryDataManager rep
3, compatListTop, -3, compatListBottom,
compatEditList,
new List>() {
- new ConsoleListBoxColumn() {
- Header = Properties.Resources.InstanceEditCompatVersionHeader,
- Width = 10,
- Renderer = v => v.ToString(),
- Comparer = (a, b) => a.CompareTo(b)
- }
+ new ConsoleListBoxColumn(
+ Properties.Resources.InstanceEditCompatVersionHeader,
+ v => v.ToString() ?? "",
+ (a, b) => a.CompareTo(b),
+ 10)
},
0, 0, ListSortDirection.Descending
);
AddObject(compatList);
compatList.AddTip("A", Properties.Resources.Add);
- compatList.AddBinding(Keys.A, (object sender, ConsoleTheme theme) => {
- CompatibleVersionDialog vd = new CompatibleVersionDialog(ksp.game);
- GameVersion newVersion = vd.Run(theme);
- DrawBackground(theme);
+ compatList.AddBinding(Keys.A, (object sender) => {
+ CompatibleVersionDialog vd = new CompatibleVersionDialog(theme, ksp.game);
+ var newVersion = vd.Run();
+ DrawBackground();
if (newVersion != null && !compatEditList.Contains(newVersion)) {
compatEditList.Add(newVersion);
compatList.SetData(compatEditList);
@@ -150,9 +167,12 @@ public GameInstanceEditScreen(GameInstanceManager mgr, RepositoryDataManager rep
return true;
});
compatList.AddTip("R", Properties.Resources.Remove, () => compatList.Selection != null);
- compatList.AddBinding(Keys.R, (object sender, ConsoleTheme theme) => {
- compatEditList.Remove(compatList.Selection);
- compatList.SetData(compatEditList);
+ compatList.AddBinding(Keys.R, (object sender) => {
+ if (compatList.Selection is GameVersion gv)
+ {
+ compatEditList.Remove(gv);
+ compatList.SetData(compatEditList);
+ }
return true;
});
@@ -167,7 +187,7 @@ public GameInstanceEditScreen(GameInstanceManager mgr, RepositoryDataManager rep
}
}
- private static V SortedDictFind(SortedDictionary dict, Func pred)
+ private static V? SortedDictFind(SortedDictionary dict, Func pred) where K: class
{
foreach (var kvp in dict) {
if (pred(kvp.Value)) {
@@ -200,8 +220,8 @@ protected override void Save()
{
if (repoEditList != null) {
// Copy the temp list of repositories to the registry
- registry.RepositoriesSet(repoEditList);
- regMgr.Save();
+ registry?.RepositoriesSet(repoEditList);
+ regMgr?.Save();
}
if (compatEditList != null) {
ksp.SetCompatibleVersions(compatEditList);
@@ -219,14 +239,14 @@ protected override void Save()
}
}
- private readonly GameInstance ksp;
- private readonly RegistryManager regMgr;
- private readonly Registry registry;
+ private readonly GameInstance ksp;
+ private readonly RegistryManager? regMgr;
+ private readonly Registry? registry;
- private readonly SortedDictionary repoEditList;
- private readonly ConsoleListBox repoList;
- private readonly List compatEditList;
- private readonly ConsoleListBox compatList;
+ private readonly SortedDictionary? repoEditList;
+ private readonly ConsoleListBox? repoList;
+ private readonly List? compatEditList;
+ private readonly ConsoleListBox? compatList;
private const int repoFrameTop = pathRow + 2;
private const int repoListTop = repoFrameTop + 2;
diff --git a/ConsoleUI/GameInstanceListScreen.cs b/ConsoleUI/GameInstanceListScreen.cs
index 685c9acd10..e2bec88bc1 100644
--- a/ConsoleUI/GameInstanceListScreen.cs
+++ b/ConsoleUI/GameInstanceListScreen.cs
@@ -17,10 +17,12 @@ public class GameInstanceListScreen : ConsoleScreen {
///
/// Initialize the screen.
///
+ /// The visual theme to use to draw the dialog
/// Game instance manager object for getting hte Instances
/// Repository data manager providing info from repos
/// If true, this is the first screen after the splash, so Ctrl+Q exits, else Esc exits
- public GameInstanceListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, bool first = false)
+ public GameInstanceListScreen(ConsoleTheme theme, GameInstanceManager mgr, RepositoryDataManager repoData, bool first = false)
+ : base(theme)
{
manager = mgr;
@@ -33,108 +35,121 @@ public GameInstanceListScreen(GameInstanceManager mgr, RepositoryDataManager rep
1, 4, -1, -2,
manager.Instances.Values,
new List>() {
- new ConsoleListBoxColumn() {
- Header = Properties.Resources.InstanceListDefaultHeader,
- Width = 7,
- Renderer = StatusSymbol
- }, new ConsoleListBoxColumn() {
- Header = Properties.Resources.InstanceListNameHeader,
- Width = 20,
- Renderer = k => k.Name
- }, new ConsoleListBoxColumn() {
- Header = Properties.Resources.InstanceListGameHeader,
- Width = 5,
- Renderer = k => k.game.ShortName
- }, new ConsoleListBoxColumn() {
- Header = Properties.Resources.InstanceListVersionHeader,
- Width = 12,
- Renderer = k => k.Version()?.ToString() ?? Properties.Resources.InstanceListNoVersion,
- Comparer = (a, b) => a.Version()?.CompareTo(b.Version() ?? GameVersion.Any) ?? 1
- }, new ConsoleListBoxColumn() {
- Header = Properties.Resources.InstanceListPathHeader,
- Width = null,
- Renderer = k => k.GameDir()
- }
+ new ConsoleListBoxColumn(
+ Properties.Resources.InstanceListDefaultHeader,
+ StatusSymbol,
+ null,
+ 7),
+ new ConsoleListBoxColumn(
+ Properties.Resources.InstanceListNameHeader,
+ k => k.Name,
+ null,
+ 20),
+ new ConsoleListBoxColumn(
+ Properties.Resources.InstanceListGameHeader,
+ k => k.game.ShortName,
+ null,
+ 5),
+ new ConsoleListBoxColumn(
+ Properties.Resources.InstanceListVersionHeader,
+ k => k.Version()?.ToString() ?? Properties.Resources.InstanceListNoVersion,
+ (a, b) => a.Version()?.CompareTo(b.Version() ?? GameVersion.Any) ?? 1,
+ 12),
+ new ConsoleListBoxColumn(
+ Properties.Resources.InstanceListPathHeader,
+ k => k.GameDir(),
+ null,
+ null)
},
1, 0, ListSortDirection.Descending
);
if (first) {
AddTip($"{Properties.Resources.Ctrl}+Q", Properties.Resources.Quit);
- AddBinding(Keys.AltX, (object sender, ConsoleTheme theme) => false);
- AddBinding(Keys.CtrlQ, (object sender, ConsoleTheme theme) => false);
+ AddBinding(Keys.AltX, (object sender) => false);
+ AddBinding(Keys.CtrlQ, (object sender) => false);
} else {
AddTip(Properties.Resources.Esc, Properties.Resources.Quit);
- AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => false);
+ AddBinding(Keys.Escape, (object sender) => false);
}
AddTip(Properties.Resources.Enter, Properties.Resources.Select);
- AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
-
- ConsoleMessageDialog d = new ConsoleMessageDialog(
- string.Format(Properties.Resources.InstanceListLoadingInstance, instanceList.Selection.Name),
- new List()
- );
-
- if (TryGetInstance(theme, instanceList.Selection, repoData,
- (ConsoleTheme th) => { d.Run(th, (ConsoleTheme thm) => {}); },
- null)) {
- try {
- manager.SetCurrentInstance(instanceList.Selection.Name);
- } catch (Exception ex) {
- // This can throw if the previous current instance had an error,
- // since it gets destructed when it's replaced.
- RaiseError(ex.Message);
+ AddBinding(Keys.Enter, (object sender) => {
+ if (instanceList.Selection is GameInstance inst)
+ {
+ var d = new ConsoleMessageDialog(theme, string.Format(Properties.Resources.InstanceListLoadingInstance,
+ inst.Name),
+ new List());
+
+ if (TryGetInstance(theme, inst, repoData,
+ (ConsoleTheme th) => { d.Run(() => {}); },
+ null)) {
+ try {
+ manager.SetCurrentInstance(inst.Name);
+ } catch (Exception ex) {
+ // This can throw if the previous current instance had an error,
+ // since it gets destructed when it's replaced.
+ RaiseError(ex.Message);
+ }
+ return false;
+ } else {
+ return true;
}
- return false;
- } else {
- return true;
}
+ return true;
});
instanceList.AddTip("A", Properties.Resources.Add);
- instanceList.AddBinding(Keys.A, (object sender, ConsoleTheme theme) => {
- LaunchSubScreen(theme, new GameInstanceAddScreen(manager));
+ instanceList.AddBinding(Keys.A, (object sender) => {
+ LaunchSubScreen(new GameInstanceAddScreen(theme, manager));
instanceList.SetData(manager.Instances.Values);
return true;
});
instanceList.AddTip("R", Properties.Resources.Remove);
- instanceList.AddBinding(Keys.R, (object sender, ConsoleTheme theme) => {
- manager.RemoveInstance(instanceList.Selection.Name);
- instanceList.SetData(manager.Instances.Values);
+ instanceList.AddBinding(Keys.R, (object sender) => {
+ if (instanceList.Selection is GameInstance inst)
+ {
+ manager.RemoveInstance(inst.Name);
+ instanceList.SetData(manager.Instances.Values);
+ }
return true;
});
instanceList.AddTip("E", Properties.Resources.Edit);
- instanceList.AddBinding(Keys.E, (object sender, ConsoleTheme theme) => {
-
- ConsoleMessageDialog d = new ConsoleMessageDialog(
- string.Format(Properties.Resources.InstanceListLoadingInstance, instanceList.Selection.Name),
- new List()
- );
- TryGetInstance(theme, instanceList.Selection, repoData,
- (ConsoleTheme th) => { d.Run(theme, (ConsoleTheme thm) => {}); },
- null);
- // Still launch the screen even if the load fails,
- // because you need to be able to fix the name/path.
- LaunchSubScreen(theme, new GameInstanceEditScreen(manager, repoData, instanceList.Selection));
-
+ instanceList.AddBinding(Keys.E, (object sender) => {
+ if (instanceList.Selection is GameInstance inst)
+ {
+ var d = new ConsoleMessageDialog(
+ theme,
+ string.Format(Properties.Resources.InstanceListLoadingInstance, inst.Name),
+ new List());
+ TryGetInstance(theme, inst, repoData,
+ (ConsoleTheme th) => { d.Run(() => {}); },
+ null);
+ // Still launch the screen even if the load fails,
+ // because you need to be able to fix the name/path.
+ LaunchSubScreen(new GameInstanceEditScreen(theme, manager, repoData, instanceList.Selection));
+ }
return true;
});
instanceList.AddTip("D", Properties.Resources.InstanceListDefaultToggle);
- instanceList.AddBinding(Keys.D, (object sender, ConsoleTheme theme) => {
- string name = instanceList.Selection.Name;
- if (name == manager.AutoStartInstance) {
- manager.ClearAutoStart();
- } else {
- try {
- manager.SetAutoStart(name);
- } catch (NotKSPDirKraken k) {
- ConsoleMessageDialog errd = new ConsoleMessageDialog(
- string.Format(Properties.Resources.InstanceListLoadingError, k.path, k.Message),
- new List() { Properties.Resources.OK }
- );
- errd.Run(theme);
+ instanceList.AddBinding(Keys.D, (object sender) => {
+ if (instanceList.Selection is GameInstance inst)
+ {
+ string name = inst.Name;
+ if (name == manager.AutoStartInstance) {
+ manager.ClearAutoStart();
+ } else {
+ try {
+ manager.SetAutoStart(name);
+ } catch (NotKSPDirKraken k) {
+ var errd = new ConsoleMessageDialog(
+ theme,
+ string.Format(Properties.Resources.InstanceListLoadingError, k.path, k.Message),
+ new List() { Properties.Resources.OK }
+ );
+ errd.Run();
+ }
}
}
return true;
@@ -148,25 +163,19 @@ public GameInstanceListScreen(GameInstanceManager mgr, RepositoryDataManager rep
/// Put CKAN 1.25.5 in top left corner
///
protected override string LeftHeader()
- {
- return $"{Meta.GetProductName()} {Meta.GetVersion()}";
- }
+ => $"{Meta.GetProductName()} {Meta.GetVersion()}";
///
/// Put description in top center
///
protected override string CenterHeader()
- {
- return Properties.Resources.InstanceListTitle;
- }
+ => Properties.Resources.InstanceListTitle;
///
/// Label the menu as Sort
///
protected override string MenuTip()
- {
- return Properties.Resources.Sort;
- }
+ => Properties.Resources.Sort;
///
/// Try to load the registry of an instance
@@ -183,7 +192,7 @@ public static bool TryGetInstance(ConsoleTheme theme,
GameInstance ksp,
RepositoryDataManager repoData,
Action render,
- IProgress progress)
+ IProgress? progress)
{
bool retry;
do {
@@ -201,13 +210,14 @@ public static bool TryGetInstance(ConsoleTheme theme,
} catch (RegistryInUseKraken k) {
ConsoleMessageDialog md = new ConsoleMessageDialog(
+ theme,
k.ToString(),
new List() {
Properties.Resources.Cancel,
Properties.Resources.Force
}
);
- if (md.Run(theme) == 1) {
+ if (md.Run() == 1) {
// Delete it
File.Delete(k.lockfilePath);
retry = true;
@@ -219,21 +229,23 @@ public static bool TryGetInstance(ConsoleTheme theme,
} catch (NotKSPDirKraken k) {
ConsoleMessageDialog errd = new ConsoleMessageDialog(
+ theme,
string.Format(Properties.Resources.InstanceListLoadingError, ksp.GameDir(), k.Message),
new List() { Properties.Resources.OK }
);
- errd.Run(theme);
+ errd.Run();
return false;
} catch (Exception e) {
ConsoleMessageDialog errd = new ConsoleMessageDialog(
+ theme,
string.Format(Properties.Resources.InstanceListLoadingError,
Platform.FormatPath(Path.Combine(ksp.CkanDir(), "registry.json")),
e.ToString()),
new List() { Properties.Resources.OK }
);
- errd.Run(theme);
+ errd.Run();
return false;
}
diff --git a/ConsoleUI/GameInstanceScreen.cs b/ConsoleUI/GameInstanceScreen.cs
index efd81ae92a..3af3d63ea9 100644
--- a/ConsoleUI/GameInstanceScreen.cs
+++ b/ConsoleUI/GameInstanceScreen.cs
@@ -13,15 +13,16 @@ public abstract class GameInstanceScreen : ConsoleScreen {
///
/// Initialize the screen
///
+ /// The visual theme to use to draw the dialog
/// Game instance manager containing the instances, needed for saving changes
/// Initial value of name field
/// Initial value of path field
- protected GameInstanceScreen(GameInstanceManager mgr, string initName = "", string initPath = "") : base()
+ protected GameInstanceScreen(ConsoleTheme theme, GameInstanceManager mgr, string initName = "", string initPath = "") : base(theme)
{
manager = mgr;
AddTip("F2", Properties.Resources.Accept);
- AddBinding(Keys.F2, (object sender, ConsoleTheme theme) => {
+ AddBinding(Keys.F2, (object sender) => {
if (Valid()) {
Save();
// Close screen
@@ -33,7 +34,7 @@ protected GameInstanceScreen(GameInstanceManager mgr, string initName = "", stri
});
AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
- AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
+ AddBinding(Keys.Escape, (object sender) => {
// Discard changes
return false;
});
@@ -55,9 +56,7 @@ protected GameInstanceScreen(GameInstanceManager mgr, string initName = "", stri
/// Put CKAN 1.25.5 in top left corner
///
protected override string LeftHeader()
- {
- return $"{Meta.GetProductName()} {Meta.GetVersion()}";
- }
+ => $"{Meta.GetProductName()} {Meta.GetVersion()}";
///
/// Return whether the fields currently are valid.
diff --git a/ConsoleUI/InstallFilterAddDialog.cs b/ConsoleUI/InstallFilterAddDialog.cs
index bd7f450ac8..a018b865c7 100644
--- a/ConsoleUI/InstallFilterAddDialog.cs
+++ b/ConsoleUI/InstallFilterAddDialog.cs
@@ -12,7 +12,8 @@ public class InstallFilterAddDialog : ConsoleDialog {
///
/// Initialize the popup
///
- public InstallFilterAddDialog() : base()
+ /// The visual theme to use to draw the dialog
+ public InstallFilterAddDialog(ConsoleTheme theme) : base(theme)
{
int l = GetLeft(),
r = GetRight();
@@ -27,13 +28,13 @@ public InstallFilterAddDialog() : base()
};
AddObject(manualEntry);
manualEntry.AddTip(Properties.Resources.Enter, Properties.Resources.FilterAddAcceptTip);
- manualEntry.AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
+ manualEntry.AddBinding(Keys.Enter, (object sender) => {
choice = manualEntry.Value;
return false;
});
AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
- AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
+ AddBinding(Keys.Escape, (object sender) => {
choice = null;
return false;
});
@@ -45,17 +46,16 @@ public InstallFilterAddDialog() : base()
/// Display the dialog and handle its interaction
///
/// Function to control the dialog, default is normal user interaction
- /// The visual theme to use to draw the dialog
///
/// User input
///
- public new string Run(ConsoleTheme theme, Action process = null)
+ public new string? Run(Action? process = null)
{
- base.Run(theme, process);
+ base.Run(process);
return choice;
}
private readonly ConsoleField manualEntry;
- private string choice;
+ private string? choice;
}
}
diff --git a/ConsoleUI/InstallFiltersScreen.cs b/ConsoleUI/InstallFiltersScreen.cs
index 0cf018885e..5edbb8091d 100644
--- a/ConsoleUI/InstallFiltersScreen.cs
+++ b/ConsoleUI/InstallFiltersScreen.cs
@@ -15,9 +15,11 @@ public class InstallFiltersScreen : ConsoleScreen {
///
/// Initialize the screen
///
+ /// The visual theme to use to draw the dialog
/// Object holding the global configuration
/// The current instance
- public InstallFiltersScreen(IConfiguration globalConfig, GameInstance instance)
+ public InstallFiltersScreen(ConsoleTheme theme, IConfiguration globalConfig, GameInstance instance)
+ : base(theme)
{
this.globalConfig = globalConfig;
this.instance = instance;
@@ -25,18 +27,18 @@ public InstallFiltersScreen(IConfiguration globalConfig, GameInstance instance)
instanceFilters = instance.InstallFilters.ToList();
AddTip("F2", Properties.Resources.Accept);
- AddBinding(Keys.F2, (object sender, ConsoleTheme theme) => {
+ AddBinding(Keys.F2, (object sender) => {
Save();
// Close screen
return false;
});
AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
- AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
+ AddBinding(Keys.Escape, (object sender) => {
// Discard changes
return false;
});
- mainMenu = new ConsolePopupMenu(new List() {
+ mainMenu = new ConsolePopupMenu(new List() {
new ConsoleMenuOption(Properties.Resources.FiltersAddMiniAVCMenu, "",
Properties.Resources.FiltersAddMiniAVCMenuTip,
true, AddMiniAVC),
@@ -48,22 +50,22 @@ public InstallFiltersScreen(IConfiguration globalConfig, GameInstance instance)
2, 2, -2, vMid - 1,
globalFilters,
new List>() {
- new ConsoleListBoxColumn() {
- Header = Properties.Resources.FiltersGlobalHeader,
- Width = null,
- Renderer = f => f,
- }
+ new ConsoleListBoxColumn(
+ Properties.Resources.FiltersGlobalHeader,
+ f => f,
+ null,
+ null)
},
0
);
AddObject(globalList);
globalList.AddTip("A", Properties.Resources.Add);
- globalList.AddBinding(Keys.A, (object sender, ConsoleTheme theme) => {
+ globalList.AddBinding(Keys.A, (object sender) => {
AddFilter(theme, globalList, globalFilters);
return true;
});
globalList.AddTip("R", Properties.Resources.Remove);
- globalList.AddBinding(Keys.R, (object sender, ConsoleTheme theme) => {
+ globalList.AddBinding(Keys.R, (object sender) => {
RemoveFilter(globalList, globalFilters);
return true;
});
@@ -71,22 +73,22 @@ public InstallFiltersScreen(IConfiguration globalConfig, GameInstance instance)
2, vMid + 1, -2, -2,
instanceFilters,
new List>() {
- new ConsoleListBoxColumn() {
- Header = Properties.Resources.FiltersInstanceHeader,
- Width = null,
- Renderer = f => f,
- }
+ new ConsoleListBoxColumn(
+ Properties.Resources.FiltersInstanceHeader,
+ f => f,
+ null,
+ null)
},
0
);
AddObject(instanceList);
instanceList.AddTip("A", Properties.Resources.Add);
- instanceList.AddBinding(Keys.A, (object sender, ConsoleTheme theme) => {
+ instanceList.AddBinding(Keys.A, (object sender) => {
AddFilter(theme, instanceList, instanceFilters);
return true;
});
instanceList.AddTip("R", Properties.Resources.Remove);
- instanceList.AddBinding(Keys.R, (object sender, ConsoleTheme theme) => {
+ instanceList.AddBinding(Keys.R, (object sender) => {
RemoveFilter(instanceList, instanceFilters);
return true;
});
@@ -96,19 +98,15 @@ public InstallFiltersScreen(IConfiguration globalConfig, GameInstance instance)
/// Put CKAN 1.25.5 in top left corner
///
protected override string LeftHeader()
- {
- return $"{Meta.GetProductName()} {Meta.GetVersion()}";
- }
+ => $"{Meta.GetProductName()} {Meta.GetVersion()}";
///
/// Put description in top center
///
protected override string CenterHeader()
- {
- return Properties.Resources.FiltersTitle;
- }
+ => Properties.Resources.FiltersTitle;
- private bool AddMiniAVC(ConsoleTheme theme)
+ private bool AddMiniAVC()
{
globalFilters = globalFilters
.Concat(miniAVC)
@@ -120,9 +118,9 @@ private bool AddMiniAVC(ConsoleTheme theme)
private void AddFilter(ConsoleTheme theme, ConsoleListBox box, List filters)
{
- string filter = new InstallFilterAddDialog().Run(theme);
- DrawBackground(theme);
- if (!string.IsNullOrEmpty(filter) && !filters.Contains(filter)) {
+ var filter = new InstallFilterAddDialog(theme).Run();
+ DrawBackground();
+ if (filter != null && !string.IsNullOrEmpty(filter) && !filters.Contains(filter)) {
filters.Add(filter);
box.SetData(filters);
}
@@ -130,8 +128,11 @@ private void AddFilter(ConsoleTheme theme, ConsoleListBox box, List box, List filters)
{
- filters.Remove(box.Selection);
- box.SetData(filters);
+ if (box.Selection is not null)
+ {
+ filters.Remove(box.Selection);
+ box.SetData(filters);
+ }
}
private void Save()
diff --git a/ConsoleUI/InstallFromCkanDialog.cs b/ConsoleUI/InstallFromCkanDialog.cs
index ea69db840d..250fee23a5 100644
--- a/ConsoleUI/InstallFromCkanDialog.cs
+++ b/ConsoleUI/InstallFromCkanDialog.cs
@@ -20,12 +20,13 @@ public static CkanModule[] ChooseCkanFiles(ConsoleTheme theme,
GameInstance gameInst)
{
var cfmsd = new ConsoleFileMultiSelectDialog(
+ theme,
Properties.Resources.CkanFileSelectTitle,
FindDownloadsPath(gameInst),
"*.ckan",
Properties.Resources.CkanFileSelectHeader,
Properties.Resources.CkanFileSelectHeader);
- return cfmsd.Run(theme)
+ return cfmsd.Run()
.Select(f => CkanModule.FromFile(f.FullName))
.ToArray();
}
diff --git a/ConsoleUI/InstallScreen.cs b/ConsoleUI/InstallScreen.cs
index d9fd9bcb1c..b5c9d707df 100644
--- a/ConsoleUI/InstallScreen.cs
+++ b/ConsoleUI/InstallScreen.cs
@@ -16,15 +16,16 @@ public class InstallScreen : ProgressScreen {
///
/// Initialize the Screen
///
+ /// The visual theme to use to draw the dialog
/// Game instance manager containing instances
/// Repository data manager providing info from repos
/// Plan of mods to install or remove
/// True if debug options should be available, false otherwise
- public InstallScreen(GameInstanceManager mgr, RepositoryDataManager repoData, ChangePlan cp, bool dbg)
+ public InstallScreen(ConsoleTheme theme, GameInstanceManager mgr, RepositoryDataManager repoData, ChangePlan cp, bool dbg)
: base(
+ theme,
Properties.Resources.InstallTitle,
- Properties.Resources.InstallMessage
- )
+ Properties.Resources.InstallMessage)
{
debug = dbg;
manager = mgr;
@@ -35,146 +36,149 @@ public InstallScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Ch
///
/// Run the screen
///
- /// The visual theme to use to draw the dialog
/// Framework parameter not used by this object
- public override void Run(ConsoleTheme theme, Action process = null)
+ public override void Run(Action? process = null)
{
HashSet rejected = new HashSet();
- DrawBackground(theme);
- using (TransactionScope trans = CkanTransaction.CreateTransactionScope()) {
- bool retry = false;
- do {
- Draw(theme);
- try {
- // Reset this so we stop unless an exception sets it to true
- retry = false;
-
- var regMgr = RegistryManager.Instance(manager.CurrentInstance, repoData);
- var registry = regMgr.registry;
-
- // GUI prompts user to choose recs/sugs,
- // CmdLine assumes recs and ignores sugs
- if (plan.Install.Count > 0) {
- // Track previously rejected optional dependencies and don't prompt for them again.
- DependencyScreen ds = new DependencyScreen(manager, registry, plan, rejected, debug);
- if (ds.HaveOptions()) {
- LaunchSubScreen(theme, ds);
+ DrawBackground();
+ if (manager.CurrentInstance != null && manager.Cache != null)
+ {
+ using (TransactionScope trans = CkanTransaction.CreateTransactionScope()) {
+ bool retry = false;
+ do {
+ Draw();
+ try {
+ // Reset this so we stop unless an exception sets it to true
+ retry = false;
+
+ var regMgr = RegistryManager.Instance(manager.CurrentInstance, repoData);
+ var registry = regMgr.registry;
+
+ // GUI prompts user to choose recs/sugs,
+ // CmdLine assumes recs and ignores sugs
+ if (plan.Install.Count > 0) {
+ // Track previously rejected optional dependencies and don't prompt for them again.
+ DependencyScreen ds = new DependencyScreen(theme, manager, registry, plan, rejected, debug);
+ if (ds.HaveOptions()) {
+ LaunchSubScreen(ds);
+ }
}
- }
- // FUTURE: BackgroundWorker
+ // FUTURE: BackgroundWorker
- HashSet possibleConfigOnlyDirs = null;
+ HashSet? possibleConfigOnlyDirs = null;
- ModuleInstaller inst = new ModuleInstaller(manager.CurrentInstance, manager.Cache, this);
- inst.onReportModInstalled += OnModInstalled;
- if (plan.Remove.Count > 0) {
- inst.UninstallList(plan.Remove, ref possibleConfigOnlyDirs, regMgr, true, new List(plan.Install));
- plan.Remove.Clear();
- }
- NetAsyncModulesDownloader dl = new NetAsyncModulesDownloader(this, manager.Cache);
- if (plan.Install.Count > 0) {
- var iList = plan.Install
- .Select(m => Utilities.DefaultIfThrows(() =>
- registry.LatestAvailable(m.identifier,
- manager.CurrentInstance.VersionCriteria(),
- null,
- registry.InstalledModules
- .Select(im => im.Module)
- .ToArray(),
- plan.Install))
- ?? m)
- .ToArray();
- inst.InstallList(iList, resolvOpts, regMgr, ref possibleConfigOnlyDirs, dl);
- plan.Install.Clear();
- }
- if (plan.Upgrade.Count > 0) {
- var upgGroups = registry
- .CheckUpgradeable(manager.CurrentInstance,
- // Hold identifiers not chosen for upgrading
- registry.Installed(false)
- .Keys
- .Except(plan.Upgrade)
- .ToHashSet());
- inst.Upgrade(upgGroups[true], dl, ref possibleConfigOnlyDirs, regMgr);
- plan.Upgrade.Clear();
- }
- if (plan.Replace.Count > 0) {
- inst.Replace(AllReplacements(plan.Replace), resolvOpts, dl, ref possibleConfigOnlyDirs, regMgr, true);
- }
+ ModuleInstaller inst = new ModuleInstaller(manager.CurrentInstance, manager.Cache, this);
+ inst.onReportModInstalled += OnModInstalled;
+ if (plan.Remove.Count > 0) {
+ inst.UninstallList(plan.Remove, ref possibleConfigOnlyDirs, regMgr, true, new List(plan.Install));
+ plan.Remove.Clear();
+ }
+ NetAsyncModulesDownloader dl = new NetAsyncModulesDownloader(this, manager.Cache);
+ if (plan.Install.Count > 0) {
+ var iList = plan.Install
+ .Select(m => Utilities.DefaultIfThrows(() =>
+ registry.LatestAvailable(m.identifier,
+ manager.CurrentInstance.VersionCriteria(),
+ null,
+ registry.InstalledModules
+ .Select(im => im.Module)
+ .ToArray(),
+ plan.Install))
+ ?? m)
+ .ToArray();
+ inst.InstallList(iList, resolvOpts, regMgr, ref possibleConfigOnlyDirs, dl);
+ plan.Install.Clear();
+ }
+ if (plan.Upgrade.Count > 0) {
+ var upgGroups = registry
+ .CheckUpgradeable(manager.CurrentInstance,
+ // Hold identifiers not chosen for upgrading
+ registry.Installed(false)
+ .Keys
+ .Except(plan.Upgrade)
+ .ToHashSet());
+ inst.Upgrade(upgGroups[true], dl, ref possibleConfigOnlyDirs, regMgr);
+ plan.Upgrade.Clear();
+ }
+ if (plan.Replace.Count > 0) {
+ inst.Replace(AllReplacements(plan.Replace), resolvOpts, dl, ref possibleConfigOnlyDirs, regMgr, true);
+ }
- trans.Complete();
- inst.onReportModInstalled -= OnModInstalled;
- // Don't let the installer re-use old screen references
- inst.User = null;
-
- HandlePossibleConfigOnlyDirs(theme, registry, possibleConfigOnlyDirs);
-
- } catch (CancelledActionKraken) {
- // Don't need to tell the user they just cancelled out.
- } catch (FileNotFoundKraken ex) {
- // Possible file corruption
- RaiseError(ex.Message);
- } catch (DirectoryNotFoundKraken ex) {
- RaiseError(ex.Message);
- } catch (FileExistsKraken ex) {
- if (ex.owningModule != null) {
- RaiseMessage(Properties.Resources.InstallOwnedFileConflict, ex.installingModule, ex.filename, ex.owningModule);
- } else {
- RaiseMessage(Properties.Resources.InstallUnownedFileConflict, ex.installingModule, ex.filename, ex.installingModule);
- }
- RaiseError(Properties.Resources.InstallFilesReverted);
- } catch (DownloadErrorsKraken ex) {
- RaiseError(ex.ToString());
- } catch (ModuleDownloadErrorsKraken ex) {
- RaiseError(ex.ToString());
- } catch (DownloadThrottledKraken ex) {
- if (RaiseYesNoDialog(string.Format(Properties.Resources.InstallAuthTokenPrompt, ex.ToString()))) {
- if (ex.infoUrl != null) {
- ModInfoScreen.LaunchURL(theme, ex.infoUrl);
+ trans.Complete();
+ inst.onReportModInstalled -= OnModInstalled;
+ // Don't let the installer re-use old screen references
+ inst.User = new NullUser();
+
+ HandlePossibleConfigOnlyDirs(theme, registry, possibleConfigOnlyDirs);
+
+ } catch (CancelledActionKraken) {
+ // Don't need to tell the user they just cancelled out.
+ } catch (FileNotFoundKraken ex) {
+ // Possible file corruption
+ RaiseError(ex.Message);
+ } catch (DirectoryNotFoundKraken ex) {
+ RaiseError(ex.Message);
+ } catch (FileExistsKraken ex) {
+ if (ex.owningModule != null) {
+ RaiseMessage(Properties.Resources.InstallOwnedFileConflict, ex.installingModule?.ToString() ?? "", ex.filename, ex.owningModule);
+ } else {
+ RaiseMessage(Properties.Resources.InstallUnownedFileConflict, ex.installingModule?.ToString() ?? "", ex.filename, ex.installingModule?.ToString() ?? "");
+ }
+ RaiseError(Properties.Resources.InstallFilesReverted);
+ } catch (DownloadErrorsKraken ex) {
+ RaiseError(ex.ToString());
+ } catch (ModuleDownloadErrorsKraken ex) {
+ RaiseError(ex.ToString());
+ } catch (DownloadThrottledKraken ex) {
+ if (RaiseYesNoDialog(string.Format(Properties.Resources.InstallAuthTokenPrompt, ex.ToString()))) {
+ if (ex.infoUrl != null) {
+ ModInfoScreen.LaunchURL(theme, ex.infoUrl);
+ }
+ LaunchSubScreen(new AuthTokenScreen(theme));
+ }
+ } catch (MissingCertificateKraken ex) {
+ RaiseError(ex.ToString());
+ } catch (InconsistentKraken ex) {
+ RaiseError(ex.Message);
+ } catch (TooManyModsProvideKraken ex) {
+
+ var ch = new ConsoleChoiceDialog(
+ theme,
+ ex.Message,
+ Properties.Resources.InstallTooManyModsNameHeader,
+ ex.modules,
+ (CkanModule mod) => mod.ToString()
+ );
+ var chosen = ch.Run();
+ DrawBackground();
+ if (chosen != null) {
+ // Use chosen to continue installing
+ plan.Install.Add(chosen);
+ retry = true;
}
- LaunchSubScreen(theme, new AuthTokenScreen());
- }
- } catch (MissingCertificateKraken ex) {
- RaiseError(ex.ToString());
- } catch (InconsistentKraken ex) {
- RaiseError(ex.Message);
- } catch (TooManyModsProvideKraken ex) {
-
- ConsoleChoiceDialog ch = new ConsoleChoiceDialog(
- ex.Message,
- Properties.Resources.InstallTooManyModsNameHeader,
- ex.modules,
- (CkanModule mod) => mod.ToString()
- );
- CkanModule chosen = ch.Run(theme);
- DrawBackground(theme);
- if (chosen != null) {
- // Use chosen to continue installing
- plan.Install.Add(chosen);
- retry = true;
- }
- } catch (BadMetadataKraken ex) {
- RaiseError(Properties.Resources.InstallBadMetadata, ex.module, ex.Message);
- } catch (DependencyNotSatisfiedKraken ex) {
- RaiseError(Properties.Resources.InstallUnsatisfiedDependency, ex.parent, ex.module, ex.Message);
- } catch (ModuleNotFoundKraken ex) {
- RaiseError(Properties.Resources.InstallModuleNotFound, ex.module, ex.Message);
- } catch (ModNotInstalledKraken ex) {
- RaiseError(Properties.Resources.InstallNotInstalled, ex.mod);
- } catch (DllLocationMismatchKraken ex) {
- RaiseError(ex.Message);
- }
- } while (retry);
+ } catch (BadMetadataKraken ex) {
+ RaiseError(Properties.Resources.InstallBadMetadata, ex.module?.ToString() ?? "", ex.Message);
+ } catch (DependencyNotSatisfiedKraken ex) {
+ RaiseError(Properties.Resources.InstallUnsatisfiedDependency, ex.parent, ex.module, ex.Message);
+ } catch (ModuleNotFoundKraken ex) {
+ RaiseError(Properties.Resources.InstallModuleNotFound, ex.module, ex.Message);
+ } catch (ModNotInstalledKraken ex) {
+ RaiseError(Properties.Resources.InstallNotInstalled, ex.mod);
+ } catch (DllLocationMismatchKraken ex) {
+ RaiseError(ex.Message);
+ }
+ } while (retry);
+ }
}
}
- private void HandlePossibleConfigOnlyDirs(ConsoleTheme theme,
- Registry registry,
- HashSet possibleConfigOnlyDirs)
+ private void HandlePossibleConfigOnlyDirs(ConsoleTheme theme,
+ Registry registry,
+ HashSet? possibleConfigOnlyDirs)
{
- if (possibleConfigOnlyDirs != null)
+ if (possibleConfigOnlyDirs != null && manager.CurrentInstance != null)
{
// Check again for registered files, since we may
// just have installed or upgraded some
@@ -185,7 +189,7 @@ private void HandlePossibleConfigOnlyDirs(ConsoleTheme theme,
.Any(relF => registry.FileOwner(relF) != null));
if (possibleConfigOnlyDirs.Count > 0)
{
- LaunchSubScreen(theme, new DeleteDirectoriesScreen(manager.CurrentInstance, possibleConfigOnlyDirs));
+ LaunchSubScreen(new DeleteDirectoriesScreen(theme, manager.CurrentInstance, possibleConfigOnlyDirs));
}
}
}
@@ -197,14 +201,16 @@ private void OnModInstalled(CkanModule mod)
private IEnumerable AllReplacements(IEnumerable identifiers)
{
- IRegistryQuerier registry = RegistryManager.Instance(manager.CurrentInstance, repoData).registry;
-
- foreach (string id in identifiers) {
- ModuleReplacement repl = registry.GetReplacement(
- id, manager.CurrentInstance.VersionCriteria()
- );
- if (repl != null) {
- yield return repl;
+ if (manager.CurrentInstance != null)
+ {
+ IRegistryQuerier registry = RegistryManager.Instance(manager.CurrentInstance, repoData).registry;
+
+ foreach (string id in identifiers) {
+ var repl = registry.GetReplacement(
+ id, manager.CurrentInstance.VersionCriteria());
+ if (repl != null) {
+ yield return repl;
+ }
}
}
}
diff --git a/ConsoleUI/ModInfoScreen.cs b/ConsoleUI/ModInfoScreen.cs
index a8370035dc..46c0d02414 100644
--- a/ConsoleUI/ModInfoScreen.cs
+++ b/ConsoleUI/ModInfoScreen.cs
@@ -16,12 +16,14 @@ public class ModInfoScreen : ConsoleScreen {
///
/// Initialize the Screen
///
+ /// The visual theme to use to draw the dialog
/// Game instance manager containing game instances
/// Registry of the current instance for finding mods
/// Plan of other mods to be added or removed
/// The module to display
/// True if debug options should be available, false otherwise
- public ModInfoScreen(GameInstanceManager mgr, Registry registry, ChangePlan cp, CkanModule m, bool dbg)
+ public ModInfoScreen(ConsoleTheme theme, GameInstanceManager mgr, Registry registry, ChangePlan cp, CkanModule m, bool dbg)
+ : base(theme)
{
debug = dbg;
mod = m;
@@ -103,7 +105,7 @@ public ModInfoScreen(GameInstanceManager mgr, Registry registry, ChangePlan cp,
th => th.LabelFg
);
tb.AddLine(mod.@abstract);
- if (!string.IsNullOrEmpty(mod.description)
+ if (mod.description != null && !string.IsNullOrEmpty(mod.description)
&& mod.description != mod.@abstract) {
tb.AddLine(mod.description);
}
@@ -111,87 +113,78 @@ public ModInfoScreen(GameInstanceManager mgr, Registry registry, ChangePlan cp,
if (!ChangePlan.IsAnyAvailable(registry, mod.identifier)) {
tb.AddLine(Properties.Resources.ModInfoUnavailableWarning);
}
- tb.AddScrollBindings(this);
+ tb.AddScrollBindings(this, theme);
AddTip(Properties.Resources.Esc, Properties.Resources.Back);
- AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => false);
+ AddBinding(Keys.Escape, (object sender) => false);
AddTip($"{Properties.Resources.Ctrl}+D", Properties.Resources.ModInfoDownloadToCache,
- () => !manager.Cache.IsMaybeCachedZip(mod) && !mod.IsDLC
+ () => manager.Cache != null && !manager.Cache.IsMaybeCachedZip(mod) && !mod.IsDLC
);
- AddBinding(Keys.CtrlD, (object sender, ConsoleTheme theme) => {
+ AddBinding(Keys.CtrlD, (object sender) => {
if (!mod.IsDLC) {
- Download(theme);
+ Download();
}
return true;
});
if (mod.resources != null) {
- List opts = new List();
+ var opts = new List();
if (mod.resources.homepage != null) {
opts.Add(new ConsoleMenuOption(
Properties.Resources.ModInfoHomePage, "", Properties.Resources.ModInfoHomePageTip,
true,
- th => LaunchURL(th, mod.resources.homepage)
- ));
+ () => LaunchURL(theme, mod.resources.homepage)));
}
if (mod.resources.repository != null) {
opts.Add(new ConsoleMenuOption(
Properties.Resources.ModInfoRepository, "", Properties.Resources.ModInfoRepositoryTip,
true,
- th => LaunchURL(th, mod.resources.repository)
- ));
+ () => LaunchURL(theme, mod.resources.repository)));
}
if (mod.resources.bugtracker != null) {
opts.Add(new ConsoleMenuOption(
Properties.Resources.ModInfoBugtracker, "", Properties.Resources.ModInfoBugtrackerTip,
true,
- th => LaunchURL(th, mod.resources.bugtracker)
- ));
+ () => LaunchURL(theme, mod.resources.bugtracker)));
}
if (mod.resources.discussions != null) {
opts.Add(new ConsoleMenuOption(
Properties.Resources.ModInfoDiscussions, "", Properties.Resources.ModInfoDiscussionsTip,
true,
- th => LaunchURL(th, mod.resources.discussions)
- ));
+ () => LaunchURL(theme, mod.resources.discussions)));
}
if (mod.resources.spacedock != null) {
opts.Add(new ConsoleMenuOption(
Properties.Resources.ModInfoSpaceDock, "", Properties.Resources.ModInfoSpaceDockTip,
true,
- th => LaunchURL(th, mod.resources.spacedock)
- ));
+ () => LaunchURL(theme, mod.resources.spacedock)));
}
if (mod.resources.curse != null) {
opts.Add(new ConsoleMenuOption(
Properties.Resources.ModInfoCurse, "", Properties.Resources.ModInfoCurseTip,
true,
- th => LaunchURL(th, mod.resources.curse)
- ));
+ () => LaunchURL(theme, mod.resources.curse)));
}
if (mod.resources.store != null) {
opts.Add(new ConsoleMenuOption(
Properties.Resources.ModInfoStore, "", Properties.Resources.ModInfoStoreTip,
true,
- th => LaunchURL(th, mod.resources.store)
- ));
+ () => LaunchURL(theme, mod.resources.store)));
}
if (mod.resources.steamstore != null) {
opts.Add(new ConsoleMenuOption(
Properties.Resources.ModInfoSteamStore, "", Properties.Resources.ModInfoSteamStoreTip,
true,
- th => LaunchURL(th, mod.resources.steamstore)
- ));
+ () => LaunchURL(theme, mod.resources.steamstore)));
}
if (debug) {
opts.Add(null);
opts.Add(new ConsoleMenuOption(
Properties.Resources.ModInfoViewMetadata, "", Properties.Resources.ModInfoViewMetadataTip,
true,
- ViewMetadata
- ));
+ ViewMetadata));
}
if (opts.Count > 0) {
@@ -204,36 +197,30 @@ public ModInfoScreen(GameInstanceManager mgr, Registry registry, ChangePlan cp,
/// Put CKAN 1.25.5 in top left corner
///
protected override string LeftHeader()
- {
- return $"{Meta.GetProductName()} {Meta.GetVersion()}";
- }
+ => $"{Meta.GetProductName()} {Meta.GetVersion()}";
///
/// Put description in top center
///
protected override string CenterHeader()
- {
- return Properties.Resources.ModInfoTitle;
- }
+ => Properties.Resources.ModInfoTitle;
///
/// Label menu as Links
///
protected override string MenuTip()
- {
- return Properties.Resources.ModInfoMenuTip;
- }
+ => Properties.Resources.ModInfoMenuTip;
- private bool ViewMetadata(ConsoleTheme theme)
+ private bool ViewMetadata()
{
- ConsoleMessageDialog md = new ConsoleMessageDialog(
+ var md = new ConsoleMessageDialog(
+ theme,
$"\"{mod.identifier}\": {registry.GetAvailableMetadata(mod.identifier)}",
new List { Properties.Resources.OK },
() => string.Format(Properties.Resources.ModInfoViewMetadataTitle, mod.name),
- TextAlign.Left
- );
- md.Run(theme);
- DrawBackground(theme);
+ TextAlign.Left);
+ md.Run();
+ DrawBackground();
return true;
}
@@ -254,8 +241,8 @@ public static bool LaunchURL(ConsoleTheme theme, Uri u)
// support launching URLs! .NET's API design has painted us into a corner.
// So instead we display a popup dialog for the garbage to print all over,
// then wait 1.5 seconds and refresh the screen when it closes.
- ConsoleMessageDialog d = new ConsoleMessageDialog(Properties.Resources.ModInfoURLLaunching, new List());
- d.Run(theme, (ConsoleTheme th) => {
+ var d = new ConsoleMessageDialog(theme, Properties.Resources.ModInfoURLLaunching, new List());
+ d.Run(() => {
Utilities.ProcessStartURL(u.ToString());
Thread.Sleep(1500);
});
@@ -264,85 +251,85 @@ public static bool LaunchURL(ConsoleTheme theme, Uri u)
private int addDependencies(int top = 8)
{
- int numDeps = mod.depends?.Count ?? 0;
- int numConfs = mod.conflicts?.Count ?? 0;
-
- if (numDeps + numConfs > 0) {
- int midL = (Console.WindowWidth / 2) - 1;
- int h = Math.Min(11, numDeps + numConfs + 2);
+ if (manager.CurrentInstance != null)
+ {
const int lblW = 16;
+ int midL = (Console.WindowWidth / 2) - 1;
int nameW = midL - 2 - lblW - 2 - 1;
- int depsH = (h - 2) * numDeps / (numDeps + numConfs);
- var upgradeableGroups = registry
- .CheckUpgradeable(manager.CurrentInstance,
- new HashSet());
-
- AddObject(new ConsoleFrame(
- 1, top, midL, top + h - 1,
- () => Properties.Resources.ModInfoDependenciesFrame,
- th => th.NormalFrameFg,
- false
- ));
- if (numDeps > 0) {
- AddObject(new ConsoleLabel(
- 3, top + 1, 3 + lblW - 1,
- () => string.Format(Properties.Resources.ModInfoRequiredLabel, numDeps),
- null,
- th => th.DimLabelFg
- ));
- ConsoleTextBox tb = new ConsoleTextBox(
- 3 + lblW, top + 1, midL - 2, top + 1 + depsH - 1, false,
- TextAlign.Left,
- th => th.MainBg,
- th => th.LabelFg
- );
- AddObject(tb);
- foreach (RelationshipDescriptor rd in mod.depends) {
- tb.AddLine(ScreenObject.TruncateLength(
- // Show install status
- ModListScreen.StatusSymbol(plan.GetModStatus(manager, registry, rd.ToString(),
- upgradeableGroups[true]))
- + rd.ToString(),
- nameW
- ));
+ var upgradeable = registry.CheckUpgradeable(manager.CurrentInstance,
+ new HashSet())
+ [true];
+ var depends = (mod.depends?.Select(dep => RelationshipString(dep, upgradeable, nameW))
+ ?? Enumerable.Empty())
+ .ToArray();
+ var conflicts = (mod.conflicts?.Select(con => RelationshipString(con, upgradeable, nameW))
+ ?? Enumerable.Empty())
+ .ToArray();
+
+ if (depends.Length + conflicts.Length > 0) {
+ int h = Math.Min(11, depends.Length + conflicts.Length + 2);
+ int depsH = (h - 2) * depends.Length / (depends.Length + conflicts.Length);
+
+ AddObject(new ConsoleFrame(
+ 1, top, midL, top + h - 1,
+ () => Properties.Resources.ModInfoDependenciesFrame,
+ th => th.NormalFrameFg,
+ false));
+ if (depends.Length > 0) {
+ AddObject(new ConsoleLabel(
+ 3, top + 1, 3 + lblW - 1,
+ () => string.Format(Properties.Resources.ModInfoRequiredLabel, depends.Length),
+ null,
+ th => th.DimLabelFg));
+ var tb = new ConsoleTextBox(
+ 3 + lblW, top + 1, midL - 2, top + 1 + depsH - 1, false,
+ TextAlign.Left,
+ th => th.MainBg,
+ th => th.LabelFg);
+ AddObject(tb);
+ foreach (var d in depends) {
+ tb.AddLine(d);
+ }
}
- }
- if (numConfs > 0) {
- AddObject(new ConsoleLabel(
- 3, top + 1 + depsH, 3 + lblW - 1,
- () => string.Format(Properties.Resources.ModInfoConflictsLabel, numConfs),
- null,
- th => th.DimLabelFg
- ));
- ConsoleTextBox tb = new ConsoleTextBox(
- 3 + lblW, top + 1 + depsH, midL - 2, top + h - 2, false,
- TextAlign.Left,
- th => th.MainBg,
- th => th.LabelFg
- );
- AddObject(tb);
- // FUTURE: Find mods that conflict with this one
- // See GUI/MainModList.cs::ComputeConflictsFromModList
- foreach (RelationshipDescriptor rd in mod.conflicts) {
- tb.AddLine(ScreenObject.TruncateLength(
- // Show install status
- ModListScreen.StatusSymbol(plan.GetModStatus(manager, registry, rd.ToString(),
- upgradeableGroups[true]))
- + rd.ToString(),
- nameW
- ));
+ if (conflicts.Length > 0) {
+ AddObject(new ConsoleLabel(
+ 3, top + 1 + depsH, 3 + lblW - 1,
+ () => string.Format(Properties.Resources.ModInfoConflictsLabel, conflicts.Length),
+ null,
+ th => th.DimLabelFg));
+ var tb = new ConsoleTextBox(
+ 3 + lblW, top + 1 + depsH, midL - 2, top + h - 2, false,
+ TextAlign.Left,
+ th => th.MainBg,
+ th => th.LabelFg);
+ AddObject(tb);
+ // FUTURE: Find mods that conflict with this one
+ // See GUI/MainModList.cs::ComputeConflictsFromModList
+ foreach (var c in conflicts) {
+ tb.AddLine(c);
+ }
}
+ return top + h - 1;
}
- return top + h - 1;
}
return top - 1;
}
+ private string RelationshipString(RelationshipDescriptor rel,
+ List upgradeable,
+ int width)
+ => ScreenObject.TruncateLength((ModListScreen.StatusSymbol(
+ rel is ModuleRelationshipDescriptor mrd
+ // Show install status
+ ? plan.GetModStatus(manager, registry,
+ mrd.name, upgradeable)
+ : InstallStatus.NotInstalled))
+ + rel.ToString(),
+ width);
+
private DateTime? InstalledOn(string identifier)
- {
// This can be null for manually installed mods
- return registry.InstalledModule(identifier)?.InstallTime;
- }
+ => registry.InstalledModule(identifier)?.InstallTime;
private int addVersionDisplay()
{
@@ -351,17 +338,18 @@ private int addVersionDisplay()
const int boxRight = -1,
boxH = 5;
- if (ChangePlan.IsAnyAvailable(registry, mod.identifier)) {
+ if (manager.CurrentInstance != null
+ && ChangePlan.IsAnyAvailable(registry, mod.identifier)) {
- List avail = registry.AvailableByIdentifier(mod.identifier).ToList();
- CkanModule inst = registry.GetInstalledVersion( mod.identifier);
- CkanModule latest = registry.LatestAvailable( mod.identifier, null);
- bool installed = registry.IsInstalled(mod.identifier, false);
- bool latestIsInstalled = inst?.Equals(latest) ?? false;
- List others = avail;
+ var inst = registry.GetInstalledVersion(mod.identifier);
+ var latest = registry.LatestAvailable(mod.identifier, null);
+ var others = registry.AvailableByIdentifier(mod.identifier)
+ .Except(new[] { inst, latest }.OfType())
+ .OfType()
+ .ToList();
- others.Remove(inst);
- others.Remove(latest);
+ bool installed = registry.IsInstalled(mod.identifier, false);
+ bool latestIsInstalled = inst?.Equals(latest) ?? false;
if (installed) {
@@ -369,12 +357,9 @@ private int addVersionDisplay()
if (latestIsInstalled) {
- ModuleReplacement mr = registry.GetReplacement(
- mod.identifier,
- manager.CurrentInstance.VersionCriteria()
- );
-
- if (mr != null) {
+ if (registry.GetReplacement(mod.identifier,
+ manager.CurrentInstance.VersionCriteria())
+ is ModuleReplacement mr) {
// Show replaced_by
addVersionBox(
@@ -382,19 +367,18 @@ private int addVersionDisplay()
() => string.Format(Properties.Resources.ModInfoReplacedBy, mr.ReplaceWith.identifier),
th => th.AlertFrameFg,
false,
- new List() {mr.ReplaceWith}
- );
+ new List() { mr.ReplaceWith });
boxTop += boxH;
addVersionBox(
boxLeft, boxTop, boxRight, boxTop + boxH - 1,
() => instTime.HasValue
- ? string.Format(Properties.Resources.ModInfoInstalledOn, instTime.Value.ToString("d"))
+ ? string.Format(Properties.Resources.ModInfoInstalledOn,
+ instTime.Value.ToString("d"))
: Properties.Resources.ModInfoInstalledManually,
th => th.ActiveFrameFg,
true,
- new List() {inst}
- );
+ new List() { inst });
boxTop += boxH;
} else {
@@ -402,17 +386,16 @@ private int addVersionDisplay()
addVersionBox(
boxLeft, boxTop, boxRight, boxTop + boxH - 1,
() => instTime.HasValue
- ? string.Format(Properties.Resources.ModInfoLatestInstalledOn, instTime.Value.ToString("d"))
+ ? string.Format(Properties.Resources.ModInfoLatestInstalledOn,
+ instTime.Value.ToString("d"))
: Properties.Resources.ModInfoLatestInstalledManually,
th => th.ActiveFrameFg,
true,
- new List() {inst}
- );
+ new List() { inst });
boxTop += boxH;
}
-
} else {
addVersionBox(
@@ -420,19 +403,18 @@ private int addVersionDisplay()
() => Properties.Resources.ModInfoLatestVersion,
th => th.AlertFrameFg,
false,
- new List() {latest}
- );
+ new List() { latest });
boxTop += boxH;
addVersionBox(
boxLeft, boxTop, boxRight, boxTop + boxH - 1,
- () => instTime.HasValue
- ? string.Format(Properties.Resources.ModInfoInstalledOn, instTime.Value.ToString("d"))
- : Properties.Resources.ModInfoInstalledManually,
+ () => instTime.HasValue
+ ? string.Format(Properties.Resources.ModInfoInstalledOn,
+ instTime.Value.ToString("d"))
+ : Properties.Resources.ModInfoInstalledManually,
th => th.ActiveFrameFg,
true,
- new List() {inst}
- );
+ new List() { inst });
boxTop += boxH;
}
@@ -440,11 +422,10 @@ private int addVersionDisplay()
addVersionBox(
boxLeft, boxTop, boxRight, boxTop + boxH - 1,
- () => Properties.Resources.ModInfoLatestVersion,
+ () => Properties.Resources.ModInfoLatestVersion,
th => th.NormalFrameFg,
false,
- new List() {latest}
- );
+ new List() { latest });
boxTop += boxH;
}
@@ -456,8 +437,7 @@ private int addVersionDisplay()
() => Properties.Resources.ModInfoOtherVersions,
th => th.NormalFrameFg,
false,
- others
- );
+ others);
boxTop += boxH;
}
@@ -474,16 +454,18 @@ private int addVersionDisplay()
: Properties.Resources.ModInfoUnavailableInstalledManually,
th => th.AlertFrameFg,
true,
- new List() {mod}
- );
+ new List() { mod });
boxTop += boxH;
}
-
return boxTop - 1;
}
- private void addVersionBox(int l, int t, int r, int b, Func title, Func color, bool doubleLine, List releases)
+ private void addVersionBox(int l, int t, int r, int b,
+ Func title,
+ Func color,
+ bool doubleLine,
+ List releases)
{
AddObject(new ConsoleFrame(
l, t, r, b,
@@ -494,7 +476,9 @@ private void addVersionBox(int l, int t, int r, int b, Func title, Func<
if (releases != null && releases.Count > 0) {
- CkanModule.GetMinMaxVersions(releases, out ModuleVersion minMod, out ModuleVersion maxMod, out GameVersion minKsp, out GameVersion maxKsp);
+ CkanModule.GetMinMaxVersions(releases,
+ out ModuleVersion? minMod, out ModuleVersion? maxMod,
+ out GameVersion? minKsp, out GameVersion? maxKsp);
AddObject(new ConsoleLabel(
l + 2, t + 1, r - 2,
() => minMod == maxMod
@@ -511,7 +495,11 @@ private void addVersionBox(int l, int t, int r, int b, Func title, Func<
));
AddObject(new ConsoleLabel(
l + 4, t + 3, r - 2,
- () => GameVersionRange.VersionSpan(manager.CurrentInstance.game, minKsp, maxKsp),
+ () => manager.CurrentInstance == null
+ ? ""
+ : GameVersionRange.VersionSpan(manager.CurrentInstance.game,
+ minKsp ?? GameVersion.Any,
+ maxKsp ?? GameVersion.Any),
null,
color
));
@@ -526,7 +514,7 @@ private string HostedOn()
var downloadHosts = mod.download
.Select(dlUri => dlUri.Host)
.Select(host =>
- hostDomains.TryGetValue(host, out string name)
+ hostDomains.TryGetValue(host, out string? name)
? name
: host);
return string.Format(Properties.Resources.ModInfoHostedOn,
@@ -567,27 +555,29 @@ private string HostedOn()
return "";
}
- private void Download(ConsoleTheme theme)
+ private void Download()
{
- ProgressScreen ps = new ProgressScreen(string.Format(Properties.Resources.ModInfoDownloading, mod.identifier));
- NetAsyncModulesDownloader dl = new NetAsyncModulesDownloader(ps, manager.Cache);
- ModuleInstaller inst = new ModuleInstaller(manager.CurrentInstance, manager.Cache, ps);
- LaunchSubScreen(
- theme,
- ps,
- (ConsoleTheme th) => {
- try {
- dl.DownloadModules(new List {mod});
- if (!manager.Cache.IsMaybeCachedZip(mod)) {
- ps.RaiseError(Properties.Resources.ModInfoDownloadFailed);
+ if (manager.CurrentInstance != null && manager.Cache != null)
+ {
+ var ps = new ProgressScreen(theme, string.Format(Properties.Resources.ModInfoDownloading, mod.identifier));
+ var dl = new NetAsyncModulesDownloader(ps, manager.Cache);
+ var inst = new ModuleInstaller(manager.CurrentInstance, manager.Cache, ps);
+ LaunchSubScreen(
+ ps,
+ () => {
+ try {
+ dl.DownloadModules(new List {mod});
+ if (!manager.Cache.IsMaybeCachedZip(mod)) {
+ ps.RaiseError(Properties.Resources.ModInfoDownloadFailed);
+ }
+ } catch (Exception ex) {
+ ps.RaiseError(Properties.Resources.ModInfoDownloadFailed, ex);
}
- } catch (Exception ex) {
- ps.RaiseError(Properties.Resources.ModInfoDownloadFailed, ex);
}
- }
- );
- // Don't let the installer re-use old screen references
- inst.User = null;
+ );
+ // Don't let the installer re-use old screen references
+ inst.User = new NullUser();
+ }
}
private static readonly Dictionary hostDomains = new Dictionary() {
diff --git a/ConsoleUI/ModListHelpDialog.cs b/ConsoleUI/ModListHelpDialog.cs
index eb9d4b0a49..ae51118f00 100644
--- a/ConsoleUI/ModListHelpDialog.cs
+++ b/ConsoleUI/ModListHelpDialog.cs
@@ -15,7 +15,7 @@ public class ModListHelpDialog : ConsoleDialog {
///
/// Initialize the screen
///
- public ModListHelpDialog() : base()
+ public ModListHelpDialog(ConsoleTheme theme) : base(theme)
{
SetDimensions(9, 3, -9, -3);
diff --git a/ConsoleUI/ModListScreen.cs b/ConsoleUI/ModListScreen.cs
index e633f34d2e..048c9283fa 100644
--- a/ConsoleUI/ModListScreen.cs
+++ b/ConsoleUI/ModListScreen.cs
@@ -10,6 +10,7 @@
using CKAN.Extensions;
using CKAN.Games;
using CKAN.Configuration;
+using CKAN.Versioning;
namespace CKAN.ConsoleUI {
@@ -21,13 +22,19 @@ public class ModListScreen : ConsoleScreen {
///
/// Initialize the screen
///
+ /// The visual theme to use to draw the dialog
/// Game instance manager object containing the current instance
/// Repository data manager providing info from repos
/// Registry manager for the current instance
/// The game of the current instance, used for getting known versions
/// True if debug options should be available, false otherwise
- /// The theme to use for the registry update flow, if needed
- public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, RegistryManager regMgr, IGame game, bool dbg, ConsoleTheme regTheme)
+ public ModListScreen(ConsoleTheme theme,
+ GameInstanceManager mgr,
+ RepositoryDataManager repoData,
+ RegistryManager regMgr,
+ IGame game,
+ bool dbg)
+ : base(theme)
{
debug = dbg;
manager = mgr;
@@ -37,41 +44,41 @@ public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Re
moduleList = new ConsoleListBox(
1, 4, -1, -2,
- GetAllMods(regTheme),
+ GetAllMods(),
new List>() {
- new ConsoleListBoxColumn() {
- Header = "",
- Width = 1,
- Renderer = StatusSymbol
- }, new ConsoleListBoxColumn() {
- Header = Properties.Resources.ModListNameHeader,
- Width = null,
- Renderer = m => m.name ?? ""
- }, new ConsoleListBoxColumn() {
- Header = Properties.Resources.ModListVersionHeader,
- Width = 10,
- Renderer = m => ModuleInstaller.StripEpoch(m.version?.ToString() ?? ""),
- Comparer = (a, b) => a.version.CompareTo(b.version)
- }, new ConsoleListBoxColumn() {
- Header = Properties.Resources.ModListMaxGameVersionHeader,
- Width = 20,
- Renderer = m => registry.LatestCompatibleGameVersion(game.KnownVersions, m.identifier)?.ToString() ?? "",
- Comparer = (a, b) => registry.LatestCompatibleGameVersion(game.KnownVersions, a.identifier).CompareTo(registry.LatestCompatibleGameVersion(game.KnownVersions, b.identifier))
- }, new ConsoleListBoxColumn() {
- Header = Properties.Resources.ModListDownloadsHeader,
- Width = 12,
- Renderer = m => repoData.GetDownloadCount(registry.Repositories.Values, m.identifier)
+ new ConsoleListBoxColumn(
+ "", StatusSymbol, null, 1),
+ new ConsoleListBoxColumn(
+ Properties.Resources.ModListNameHeader,
+ m => m.name ?? "",
+ null, null),
+ new ConsoleListBoxColumn(
+ Properties.Resources.ModListVersionHeader,
+ m => ModuleInstaller.StripEpoch(m.version?.ToString() ?? ""),
+ (a, b) => a.version.CompareTo(b.version),
+ 10),
+ new ConsoleListBoxColumn(
+ Properties.Resources.ModListMaxGameVersionHeader,
+ m => registry.LatestCompatibleGameVersion(game.KnownVersions, m.identifier)?.ToString() ?? "",
+ (a, b) => registry.LatestCompatibleGameVersion(game.KnownVersions, a.identifier) is GameVersion gvA
+ && registry.LatestCompatibleGameVersion(game.KnownVersions, b.identifier) is GameVersion gvB
+ ? gvA.CompareTo(gvB)
+ : 0,
+ 20),
+ new ConsoleListBoxColumn(
+ Properties.Resources.ModListDownloadsHeader,
+ m => repoData.GetDownloadCount(registry.Repositories.Values, m.identifier)
?.ToString()
?? "",
- Comparer = (a, b) => (repoData.GetDownloadCount(registry.Repositories.Values, a.identifier) ?? 0)
+ (a, b) => (repoData.GetDownloadCount(registry.Repositories.Values, a.identifier) ?? 0)
.CompareTo(repoData.GetDownloadCount(registry.Repositories.Values, b.identifier) ?? 0),
- }
+ 12),
},
1, 0, ListSortDirection.Descending,
(CkanModule m, string filter) => {
// Search for author
if (filter.StartsWith("@")) {
- string authorFilt = filter.Substring(1);
+ string authorFilt = filter[1..];
if (string.IsNullOrEmpty(authorFilt)) {
return true;
} else {
@@ -84,8 +91,7 @@ public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Re
if (filter.Length <= 1) {
// Don't blank the list for just "~" by itself
return true;
- } else
- {
+ } else {
switch (filter.Substring(1, 1)) {
case "i":
return registry.IsInstalled(m.identifier, false);
@@ -96,7 +102,7 @@ public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Re
return recent.Contains(m.identifier);
case "c":
if (m.conflicts != null) {
- string conflictsWith = filter.Substring(2);
+ string conflictsWith = filter[2..];
// Search for mods depending on a given mod
foreach (var rel in m.conflicts) {
if (rel.StartsWith(conflictsWith)) {
@@ -107,7 +113,7 @@ public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Re
return false;
case "d":
if (m.depends != null) {
- string dependsOn = filter.Substring(2);
+ string dependsOn = filter[2..];
// Search for mods depending on a given mod
foreach (var rel in m.depends) {
if (rel.StartsWith(dependsOn)) {
@@ -116,7 +122,7 @@ public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Re
}
}
return false;
- }
+ }
}
return false;
@@ -147,34 +153,34 @@ public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Re
AddObject(searchBox);
AddObject(moduleList);
- AddBinding(Keys.CtrlP, (object sender, ConsoleTheme theme) => PlayGame());
- AddBinding(Keys.CtrlQ, (object sender, ConsoleTheme theme) => false);
- AddBinding(Keys.AltX, (object sender, ConsoleTheme theme) => false);
- AddBinding(Keys.F1, (object sender, ConsoleTheme theme) => Help(theme));
- AddBinding(Keys.AltH, (object sender, ConsoleTheme theme) => Help(theme));
- AddBinding(Keys.F5, (object sender, ConsoleTheme theme) => UpdateRegistry(theme));
- AddBinding(Keys.CtrlR, (object sender, ConsoleTheme theme) => UpdateRegistry(theme));
- AddBinding(Keys.CtrlU, (object sender, ConsoleTheme theme) => UpgradeAll(theme));
+ AddBinding(Keys.CtrlP, (object sender) => PlayGame());
+ 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, ConsoleTheme theme) => {
+ searchBox.AddBinding(Keys.PageUp, (object sender) => {
SetFocus(moduleList);
return true;
});
- searchBox.AddBinding(Keys.PageDown, (object sender, ConsoleTheme theme) => {
+ searchBox.AddBinding(Keys.PageDown, (object sender) => {
SetFocus(moduleList);
return true;
});
- searchBox.AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
+ searchBox.AddBinding(Keys.Enter, (object sender) => {
SetFocus(moduleList);
return true;
});
- moduleList.AddBinding(Keys.CtrlF, (object sender, ConsoleTheme theme) => {
+ moduleList.AddBinding(Keys.CtrlF, (object sender) => {
SetFocus(searchBox);
return true;
});
- moduleList.AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
+ moduleList.AddBinding(Keys.Escape, (object sender) => {
searchBox.Clear();
return true;
});
@@ -182,9 +188,9 @@ public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Re
moduleList.AddTip(Properties.Resources.Enter, Properties.Resources.Details,
() => moduleList.Selection != null
);
- moduleList.AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
+ moduleList.AddBinding(Keys.Enter, (object sender) => {
if (moduleList.Selection != null) {
- LaunchSubScreen(theme, new ModInfoScreen(manager, registry, plan, moduleList.Selection, debug));
+ LaunchSubScreen(new ModInfoScreen(theme, manager, registry, plan, moduleList.Selection, debug));
}
return true;
});
@@ -201,10 +207,11 @@ public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Re
);
moduleList.AddTip("+", Properties.Resources.ModListReplaceTip,
() => moduleList.Selection != null
- && registry.GetReplacement(moduleList.Selection.identifier, manager.CurrentInstance.VersionCriteria()) != null
+ && manager.CurrentInstance != null
+ && registry.GetReplacement(moduleList.Selection.identifier, manager.CurrentInstance.VersionCriteria()) != null
);
- moduleList.AddBinding(Keys.Plus, (object sender, ConsoleTheme theme) => {
- if (moduleList.Selection != null && !moduleList.Selection.IsDLC) {
+ moduleList.AddBinding(Keys.Plus, (object sender) => {
+ if (moduleList.Selection != null && !moduleList.Selection.IsDLC && manager.CurrentInstance != null) {
if (!registry.IsInstalled(moduleList.Selection.identifier, false)) {
plan.ToggleInstall(moduleList.Selection);
} else if (registry.IsInstalled(moduleList.Selection.identifier, false)
@@ -222,7 +229,7 @@ public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Re
&& registry.IsInstalled(moduleList.Selection.identifier, false)
&& !registry.IsAutodetected(moduleList.Selection.identifier)
);
- moduleList.AddBinding(Keys.Minus, (object sender, ConsoleTheme theme) => {
+ moduleList.AddBinding(Keys.Minus, (object sender) => {
if (moduleList.Selection != null && !moduleList.Selection.IsDLC
&& registry.IsInstalled(moduleList.Selection.identifier, false)
&& !registry.IsAutodetected(moduleList.Selection.identifier)) {
@@ -239,18 +246,21 @@ public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Re
() => moduleList.Selection != null && !moduleList.Selection.IsDLC
&& (registry.InstalledModule(moduleList.Selection.identifier)?.AutoInstalled ?? false)
);
- moduleList.AddBinding(Keys.F8, (object sender, ConsoleTheme theme) => {
- InstalledModule im = registry.InstalledModule(moduleList.Selection.identifier);
- if (im != null && !moduleList.Selection.IsDLC) {
- im.AutoInstalled = !im.AutoInstalled;
- regMgr.Save(false);
+ moduleList.AddBinding(Keys.F8, (object sender) => {
+ if (moduleList.Selection is CkanModule m)
+ {
+ var im = registry.InstalledModule(m.identifier);
+ if (im != null && !m.IsDLC) {
+ im.AutoInstalled = !im.AutoInstalled;
+ regMgr.Save(false);
+ }
}
return true;
});
AddTip("F9", Properties.Resources.ModListApplyChangesTip, plan.NonEmpty);
- AddBinding(Keys.F9, (object sender, ConsoleTheme theme) => {
- ApplyChanges(theme);
+ AddBinding(Keys.F9, (object sender) => {
+ ApplyChanges();
return true;
});
@@ -278,22 +288,24 @@ public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Re
}
));
- List opts = new List() {
+ var opts = new List() {
new ConsoleMenuOption(Properties.Resources.ModListPlayMenu, "",
Properties.Resources.ModListPlayMenuTip,
true, null, null, null, true,
() => new ConsolePopupMenu(
- manager.CurrentInstance
- .game
- .DefaultCommandLines(manager.SteamLibrary,
- new DirectoryInfo(manager.CurrentInstance.GameDir()))
- .Select((cmd, i) => new ConsoleMenuOption(
- cmd,
- i == 0 ? $"{Properties.Resources.Ctrl}+P"
- : "",
- cmd, true,
- th => PlayGame(cmd)))
- .ToList())),
+ (manager.CurrentInstance
+ ?.game
+ .DefaultCommandLines(manager.SteamLibrary,
+ new DirectoryInfo(manager.CurrentInstance.GameDir()))
+ ?? Enumerable.Empty())
+ .Select((cmd, i) => new ConsoleMenuOption(
+ cmd,
+ i == 0 ? $"{Properties.Resources.Ctrl}+P"
+ : "",
+ cmd, true,
+ () => PlayGame(cmd)))
+ .OfType()
+ .ToList())),
null,
new ConsoleMenuOption(Properties.Resources.ModListSortMenu, "",
Properties.Resources.ModListSortMenuTip,
@@ -301,7 +313,7 @@ public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Re
null,
new ConsoleMenuOption(Properties.Resources.ModListRefreshMenu, $"F5, {Properties.Resources.Ctrl}+R",
Properties.Resources.ModListRefreshMenuTip,
- true, (ConsoleTheme th) => UpdateRegistry(th)),
+ true, () => UpdateRegistry()),
new ConsoleMenuOption(Properties.Resources.ModListUpgradeMenu, $"{Properties.Resources.Ctrl}+U",
Properties.Resources.ModListUpgradeMenuTip,
true, UpgradeAll, null, null, HasAnyUpgradeable()),
@@ -337,7 +349,7 @@ public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Re
null,
new ConsoleMenuOption(Properties.Resources.ModListQuitMenu, $"{Properties.Resources.Ctrl}+Q",
Properties.Resources.ModListQuitMenuTip,
- true, (ConsoleTheme th) => false)
+ true, () => false)
};
if (debug) {
opts.Add(null);
@@ -352,17 +364,13 @@ public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Re
/// Put CKAN 1.25.5 in top left corner
///
protected override string LeftHeader()
- {
- return $"{Meta.GetProductName()} {Meta.GetVersion()}";
- }
+ => $"{Meta.GetProductName()} {Meta.GetVersion()}";
///
/// Put description in top center
///
protected override string CenterHeader()
- {
- return $"{manager.CurrentInstance.game.ShortName} {manager.CurrentInstance.Version()} ({manager.CurrentInstance.Name})";
- }
+ => $"{manager.CurrentInstance?.game.ShortName} {manager.CurrentInstance?.Version()} ({manager.CurrentInstance?.Name})";
// Alt+H doesn't work on Mac, but F1 does, and we need
// an option other than F1 for terminals that open their own help.
@@ -370,47 +378,49 @@ protected override string CenterHeader()
? "F1"
: $"F1, {Properties.Resources.Alt}+H";
- private bool ImportDownloads(ConsoleTheme theme)
+ private bool ImportDownloads()
{
- DownloadImportDialog.ImportDownloads(theme, manager.CurrentInstance, repoData, manager.Cache, plan);
- RefreshList(theme);
+ if (manager.CurrentInstance != null && manager.Cache != null)
+ {
+ DownloadImportDialog.ImportDownloads(theme, manager.CurrentInstance, repoData, manager.Cache, plan);
+ RefreshList();
+ }
return true;
}
- private bool CaptureKey(ConsoleTheme theme)
+ private bool CaptureKey()
{
ConsoleKeyInfo k = default;
- ConsoleMessageDialog keyprompt = new ConsoleMessageDialog(Properties.Resources.ModListPressAKey, new List());
- keyprompt.Run(theme, (ConsoleTheme th) => {
+ ConsoleMessageDialog keyprompt = new ConsoleMessageDialog(theme, Properties.Resources.ModListPressAKey, new List());
+ keyprompt.Run(() => {
k = Console.ReadKey(true);
});
ConsoleMessageDialog output = new ConsoleMessageDialog(
+ theme,
$"Key: {k.Key,18}\nKeyChar: 0x{(int)k.KeyChar:x2}\nModifiers: {k.Modifiers,12}",
new List { Properties.Resources.OK }
);
- output.Run(theme);
+ output.Run();
return true;
}
private bool HasAnyUpgradeable()
- {
- return (upgradeableGroups?[true].Count ?? 0) > 0;
- }
+ => (upgradeableGroups?[true].Count ?? 0) > 0;
- private bool UpgradeAll(ConsoleTheme theme)
+ private bool UpgradeAll()
{
plan.Upgrade.UnionWith(upgradeableGroups?[true].Select(m => m.identifier)
?? Enumerable.Empty());
return true;
}
- private bool ViewSuggestions(ConsoleTheme theme)
+ private bool ViewSuggestions()
{
ChangePlan reinstall = new ChangePlan();
foreach (InstalledModule im in registry.InstalledModules) {
// Only check mods that are still available
try {
- if (registry.LatestAvailable(im.identifier, manager.CurrentInstance.VersionCriteria()) != null) {
+ if (registry.LatestAvailable(im.identifier, manager.CurrentInstance?.VersionCriteria()) != null) {
reinstall.Install.Add(im.Module);
}
} catch {
@@ -418,9 +428,9 @@ private bool ViewSuggestions(ConsoleTheme theme)
}
}
try {
- DependencyScreen ds = new DependencyScreen(manager, registry, reinstall, new HashSet(), debug);
+ DependencyScreen ds = new DependencyScreen(theme, manager, registry, reinstall, new HashSet(), debug);
if (ds.HaveOptions()) {
- LaunchSubScreen(theme, ds);
+ LaunchSubScreen(ds);
bool needRefresh = false;
// Copy the right ones into our real plan
foreach (CkanModule mod in reinstall.Install) {
@@ -430,7 +440,7 @@ private bool ViewSuggestions(ConsoleTheme theme)
}
}
if (needRefresh) {
- RefreshList(theme);
+ RefreshList();
}
} else {
RaiseError(Properties.Resources.ModListAuditNotFound);
@@ -442,50 +452,49 @@ private bool ViewSuggestions(ConsoleTheme theme)
}
private bool PlayGame()
- => PlayGame(manager.CurrentInstance
- .game
- .DefaultCommandLines(manager.SteamLibrary,
- new DirectoryInfo(manager.CurrentInstance.GameDir()))
- .FirstOrDefault());
+ => manager.CurrentInstance != null
+ && PlayGame(manager.CurrentInstance
+ .game
+ .DefaultCommandLines(manager.SteamLibrary,
+ new DirectoryInfo(manager.CurrentInstance.GameDir()))
+ .First());
private bool PlayGame(string commandLine)
{
- manager.CurrentInstance.PlayGame(commandLine);
+ manager.CurrentInstance?.PlayGame(commandLine);
return true;
}
- private bool UpdateRegistry(ConsoleTheme theme, bool showNewModsPrompt = true)
+ private bool UpdateRegistry(bool showNewModsPrompt = true)
{
ProgressScreen ps = new ProgressScreen(
+ theme,
Properties.Resources.ModListUpdateRegistryTitle,
- Properties.Resources.ModListUpdateRegistryMessage
- );
- LaunchSubScreen(theme, ps, (ConsoleTheme th) => {
- HashSet availBefore = new HashSet(
- Array.ConvertAll(
- registry.CompatibleModules(
+ Properties.Resources.ModListUpdateRegistryMessage);
+ LaunchSubScreen(ps, () => {
+ if (manager.CurrentInstance != null)
+ {
+ var availBefore = registry.CompatibleModules(manager.CurrentInstance.VersionCriteria())
+ .Select(l => l.identifier)
+ .ToHashSet();
+ recent.Clear();
+ try {
+ repoData.Update(registry.Repositories.Values.ToArray(),
+ manager.CurrentInstance.game,
+ false,
+ new NetAsyncDownloader(ps),
+ ps);
+ } catch (Exception ex) {
+ // There can be errors while you re-install mods with changed metadata
+ ps.RaiseError(ex.Message + ex.StackTrace);
+ }
+ // Update recent with mods that were updated in this pass
+ foreach (CkanModule mod in registry.CompatibleModules(
manager.CurrentInstance.VersionCriteria()
- ).ToArray(),
- l => l.identifier
- )
- );
- recent.Clear();
- try {
- repoData.Update(registry.Repositories.Values.ToArray(),
- manager.CurrentInstance.game,
- false,
- new NetAsyncDownloader(ps),
- ps);
- } catch (Exception ex) {
- // There can be errors while you re-install mods with changed metadata
- ps.RaiseError(ex.Message + ex.StackTrace);
- }
- // Update recent with mods that were updated in this pass
- foreach (CkanModule mod in registry.CompatibleModules(
- manager.CurrentInstance.VersionCriteria()
- )) {
- if (!availBefore.Contains(mod.identifier)) {
- recent.Add(mod.identifier);
+ )) {
+ if (!availBefore.Contains(mod.identifier)) {
+ recent.Add(mod.identifier);
+ }
}
}
});
@@ -493,7 +502,7 @@ private bool UpdateRegistry(ConsoleTheme theme, bool showNewModsPrompt = true)
searchBox.Clear();
moduleList.FilterString = searchBox.Value = "~n";
}
- RefreshList(theme);
+ RefreshList();
return true;
}
@@ -515,148 +524,167 @@ private bool ScanForMods()
return true;
}
- private bool InstanceSettings(ConsoleTheme theme)
+ private bool InstanceSettings()
{
- var prevRepos = new SortedDictionary(registry.Repositories);
- var prevVerCrit = manager.CurrentInstance.VersionCriteria();
- LaunchSubScreen(theme, new GameInstanceEditScreen(manager, repoData, manager.CurrentInstance));
- if (!registry.Repositories.DictionaryEquals(prevRepos)) {
- // Repos changed, need to fetch them
- UpdateRegistry(theme, false);
- RefreshList(theme);
- } else if (!manager.CurrentInstance.VersionCriteria().Equals(prevVerCrit)) {
- // VersionCriteria changed, need to re-check what is compatible
- RefreshList(theme);
+ if (manager.CurrentInstance != null)
+ {
+ var prevRepos = new SortedDictionary(registry.Repositories);
+ var prevVerCrit = manager.CurrentInstance.VersionCriteria();
+ LaunchSubScreen(new GameInstanceEditScreen(theme, manager, repoData, manager.CurrentInstance));
+ if (!registry.Repositories.DictionaryEquals(prevRepos)) {
+ // Repos changed, need to fetch them
+ UpdateRegistry(false);
+ RefreshList();
+ } else if (!manager.CurrentInstance.VersionCriteria().Equals(prevVerCrit)) {
+ // VersionCriteria changed, need to re-check what is compatible
+ RefreshList();
+ }
}
return true;
}
- private bool SelectInstall(ConsoleTheme theme)
+ private bool SelectInstall()
{
- GameInstance prevInst = manager.CurrentInstance;
- var prevRepos = new SortedDictionary(registry.Repositories);
- var prevVerCrit = prevInst.VersionCriteria();
- LaunchSubScreen(theme, new GameInstanceListScreen(manager, repoData));
- if (!prevInst.Equals(manager.CurrentInstance)) {
- // Game instance changed, reset everything
- plan.Reset();
- regMgr = RegistryManager.Instance(manager.CurrentInstance, repoData);
- registry = regMgr.registry;
- RefreshList(theme);
- } else if (!registry.Repositories.DictionaryEquals(prevRepos)) {
- // Repos changed, need to fetch them
- UpdateRegistry(theme, false);
- RefreshList(theme);
- } else if (!manager.CurrentInstance.VersionCriteria().Equals(prevVerCrit)) {
- // VersionCriteria changed, need to re-check what is compatible
- RefreshList(theme);
+ if (manager.CurrentInstance != null)
+ {
+ var prevInst = manager.CurrentInstance;
+ var prevRepos = new SortedDictionary(registry.Repositories);
+ var prevVerCrit = prevInst.VersionCriteria();
+ LaunchSubScreen(new GameInstanceListScreen(theme, manager, repoData));
+ if (!prevInst.Equals(manager.CurrentInstance)) {
+ // Game instance changed, reset everything
+ plan.Reset();
+ regMgr = RegistryManager.Instance(manager.CurrentInstance, repoData);
+ registry = regMgr.registry;
+ RefreshList();
+ } else if (!registry.Repositories.DictionaryEquals(prevRepos)) {
+ // Repos changed, need to fetch them
+ UpdateRegistry(false);
+ RefreshList();
+ } else if (!manager.CurrentInstance.VersionCriteria().Equals(prevVerCrit)) {
+ // VersionCriteria changed, need to re-check what is compatible
+ RefreshList();
+ }
}
return true;
}
- private bool EditAuthTokens(ConsoleTheme theme)
+ private bool EditAuthTokens()
{
- LaunchSubScreen(theme, new AuthTokenScreen());
+ LaunchSubScreen(new AuthTokenScreen(theme));
return true;
}
- private bool EditInstallFilters(ConsoleTheme theme)
+ private bool EditInstallFilters()
{
- LaunchSubScreen(theme, new InstallFiltersScreen(
- ServiceLocator.Container.Resolve(),
- manager.CurrentInstance
- ));
+ if (manager.CurrentInstance != null)
+ {
+ LaunchSubScreen(new InstallFiltersScreen(
+ theme,
+ ServiceLocator.Container.Resolve(),
+ manager.CurrentInstance));
+ }
return true;
}
- private void RefreshList(ConsoleTheme theme)
+ private void RefreshList()
{
// In the constructor this is called while moduleList is being populated, just do nothing in this case.
// ModListScreen -> moduleList = (GetAllMods ...) -> UpdateRegistry -> RefreshList
- moduleList?.SetData(GetAllMods(theme, true));
+ moduleList?.SetData(GetAllMods(true));
}
- private List allMods = null;
+ private List? allMods = null;
- private List GetAllMods(ConsoleTheme theme, bool force = false)
+ private List GetAllMods(bool force = false)
{
- timeSinceUpdate = repoData.LastUpdate(registry.Repositories.Values);
- ScanForMods();
- if (allMods == null || force) {
- if (!registry?.HasAnyAvailable() ?? false)
- {
- UpdateRegistry(theme, false);
- }
- var crit = manager.CurrentInstance.VersionCriteria();
- allMods = new List(registry.CompatibleModules(crit));
- foreach (InstalledModule im in registry.InstalledModules) {
- var m = Utilities.DefaultIfThrows(() => registry.LatestAvailable(im.identifier, crit));
- if (m == null) {
- // Add unavailable installed mods to the list
- allMods.Add(im.Module);
+ if (manager.CurrentInstance != null)
+ {
+ timeSinceUpdate = repoData.LastUpdate(registry.Repositories.Values);
+ ScanForMods();
+ if (allMods == null || force) {
+ if (!registry.HasAnyAvailable())
+ {
+ UpdateRegistry(false);
+ }
+ var crit = manager.CurrentInstance.VersionCriteria();
+ allMods = new List(registry.CompatibleModules(crit));
+ foreach (InstalledModule im in registry.InstalledModules) {
+ var m = Utilities.DefaultIfThrows(() => registry.LatestAvailable(im.identifier, crit));
+ if (m == null) {
+ // Add unavailable installed mods to the list
+ allMods.Add(im.Module);
+ }
}
+ upgradeableGroups = registry
+ .CheckUpgradeable(manager.CurrentInstance, new HashSet());
}
- upgradeableGroups = registry
- .CheckUpgradeable(manager.CurrentInstance, new HashSet());
+ return allMods;
}
- return allMods;
+ return new List();
}
- private bool ExportInstalled(ConsoleTheme theme)
+ private bool ExportInstalled()
{
try {
- // Save the mod list as "depends" without the installed versions.
- // Because that's supposed to work.
- regMgr.Save(true);
- string path = Path.Combine(
- Platform.FormatPath(manager.CurrentInstance.CkanDir()),
- $"{Properties.Resources.ModListExportPrefix}-{manager.CurrentInstance.Name}.ckan"
- );
- RaiseError(Properties.Resources.ModListExported, path);
+ if (manager.CurrentInstance != null)
+ {
+ // Save the mod list as "depends" without the installed versions.
+ // Because that's supposed to work.
+ regMgr.Save(true);
+ string path = Path.Combine(
+ Platform.FormatPath(manager.CurrentInstance.CkanDir()),
+ $"{Properties.Resources.ModListExportPrefix}-{manager.CurrentInstance.Name}.ckan");
+ RaiseError(Properties.Resources.ModListExported, path);
+ }
} catch (Exception ex) {
RaiseError(Properties.Resources.ModListExportFailed, ex.Message);
}
return true;
}
- private bool InstallFromCkan(ConsoleTheme theme)
+ private bool InstallFromCkan()
{
- var modules = InstallFromCkanDialog.ChooseCkanFiles(theme, manager.CurrentInstance);
- if (modules.Length > 0) {
- var crit = manager.CurrentInstance.VersionCriteria();
- var installed = regMgr.registry.InstalledModules.Select(inst => inst.Module).ToList();
- var cp = new ChangePlan();
- cp.Install.UnionWith(
- modules.Concat(
- modules.Where(m => m.IsMetapackage && m.depends != null)
- .SelectMany(m => m.depends.Where(rel => !rel.MatchesAny(installed, null, null))
- .Select(rel =>
- // If there's a compatible match, return it
- // Metapackages aren't intending to prompt users to choose providing mods
- rel.ExactMatch(regMgr.registry, crit, installed, modules)
- // Otherwise look for incompatible
- ?? rel.ExactMatch(regMgr.registry, null, installed, modules))
- .Where(mod => mod != null))));
- LaunchSubScreen(theme, new InstallScreen(manager, repoData, cp, debug));
- RefreshList(theme);
+ if (manager.CurrentInstance != null)
+ {
+ var modules = InstallFromCkanDialog.ChooseCkanFiles(theme, manager.CurrentInstance);
+ if (modules.Length > 0) {
+ var crit = manager.CurrentInstance.VersionCriteria();
+ var installed = regMgr.registry.InstalledModules.Select(inst => inst.Module).ToList();
+ var cp = new ChangePlan();
+ cp.Install.UnionWith(
+ modules.Concat(
+ modules.Where(m => m.IsMetapackage && m.depends != null)
+ .SelectMany(m => m.depends?.Where(rel => !rel.MatchesAny(installed, null, null))
+ .Select(rel =>
+ // If there's a compatible match, return it
+ // Metapackages aren't intending to prompt users to choose providing mods
+ rel.ExactMatch(regMgr.registry, crit, installed, modules)
+ // Otherwise look for incompatible
+ ?? rel.ExactMatch(regMgr.registry, null, installed, modules))
+ .OfType()
+ ?? Enumerable.Empty())));
+ LaunchSubScreen(new InstallScreen(theme, manager, repoData, cp, debug));
+ RefreshList();
+ }
}
return true;
}
- private bool Help(ConsoleTheme theme)
+ private bool Help()
{
- ModListHelpDialog hd = new ModListHelpDialog();
- hd.Run(theme);
- DrawBackground(theme);
+ ModListHelpDialog hd = new ModListHelpDialog(theme);
+ hd.Run();
+ DrawBackground();
return true;
}
- private bool ApplyChanges(ConsoleTheme theme)
+ private bool ApplyChanges()
{
if (plan.NonEmpty())
{
- LaunchSubScreen(theme, new InstallScreen(manager, repoData, plan, debug));
- RefreshList(theme);
+ LaunchSubScreen(new InstallScreen(theme, manager, repoData, plan, debug));
+ RefreshList();
}
return true;
}
@@ -670,10 +698,8 @@ private bool ApplyChanges(ConsoleTheme theme)
/// String containing symbol to use
///
public string StatusSymbol(CkanModule m)
- {
- return StatusSymbol(plan.GetModStatus(manager, registry, m.identifier,
- upgradeableGroups?[true] ?? new List()));
- }
+ => StatusSymbol(plan.GetModStatus(manager, registry, m.identifier,
+ upgradeableGroups?[true] ?? new List()));
///
/// Return the symbol to use to represent a mod's StatusSymbol.
@@ -712,13 +738,13 @@ private long totalInstalledDownloadSize()
return total;
}
- private readonly GameInstanceManager manager;
- private RegistryManager regMgr;
- private Registry registry;
- private readonly RepositoryDataManager repoData;
- private Dictionary> upgradeableGroups;
- private readonly bool debug;
- private TimeSpan timeSinceUpdate = TimeSpan.Zero;
+ private readonly GameInstanceManager manager;
+ private RegistryManager regMgr;
+ private Registry registry;
+ private readonly RepositoryDataManager repoData;
+ private Dictionary>? upgradeableGroups;
+ private readonly bool debug;
+ private TimeSpan timeSinceUpdate = TimeSpan.Zero;
private readonly ConsoleField searchBox;
private readonly ConsoleListBox moduleList;
@@ -846,7 +872,8 @@ public InstallStatus GetModStatus(GameInstanceManager manager,
return InstallStatus.AutoDetected;
} else if (Replace.Contains(identifier)) {
return InstallStatus.Replacing;
- } else if (registry.GetReplacement(identifier, manager.CurrentInstance.VersionCriteria()) != null) {
+ } else if (manager.CurrentInstance != null
+ && registry.GetReplacement(identifier, manager.CurrentInstance.VersionCriteria()) != null) {
return InstallStatus.Replaceable;
} else if (!IsAnyAvailable(registry, identifier)) {
return InstallStatus.Unavailable;
diff --git a/ConsoleUI/Program.cs b/ConsoleUI/Program.cs
index da14b854ed..d30504353f 100644
--- a/ConsoleUI/Program.cs
+++ b/ConsoleUI/Program.cs
@@ -31,7 +31,7 @@ public static void Main(string[] args)
///
/// Process exit status
///
- public static int Main_(GameInstanceManager manager, string themeName, bool debug = false)
+ public static int Main_(GameInstanceManager? manager, string? themeName, bool debug = false)
{
Logging.Initialize();
diff --git a/ConsoleUI/ProgressScreen.cs b/ConsoleUI/ProgressScreen.cs
index f684f03f4c..54f1f65e56 100644
--- a/ConsoleUI/ProgressScreen.cs
+++ b/ConsoleUI/ProgressScreen.cs
@@ -15,9 +15,11 @@ public class ProgressScreen : ConsoleScreen {
///
/// Initialize the Screen
///
+ /// The visual theme to use to draw the dialog
/// Description of the task being done for the header
/// Starting string to put in the progress bar
- public ProgressScreen(string descrip, string initMsg = "")
+ public ProgressScreen(ConsoleTheme theme, string descrip, string initMsg = "")
+ : base(theme)
{
// A nice frame to take up some of the blank space at the top
AddObject(new ConsoleDoubleFrame(
@@ -25,16 +27,13 @@ public ProgressScreen(string descrip, string initMsg = "")
() => Properties.Resources.ProgressTitle,
() => Properties.Resources.ProgressMessages,
// Cheating because our IUser handler needs a theme context
- th => { yesNoTheme = th; return th.NormalFrameFg; }
- ));
+ th => th.NormalFrameFg));
progress = new ConsoleProgressBar(
3, 5, -3,
() => topMessage,
- () => percent
- );
+ () => percent);
messages = new ConsoleTextBox(
- 3, 10, -3, -3
- );
+ 3, 10, -3, -3);
AddObject(progress);
AddObject(messages);
@@ -47,17 +46,13 @@ public ProgressScreen(string descrip, string initMsg = "")
/// Put CKAN 1.25.5 in upper left corner
///
protected override string LeftHeader()
- {
- return $"{Meta.GetProductName()} {Meta.GetVersion()}";
- }
+ => $"{Meta.GetProductName()} {Meta.GetVersion()}";
///
/// Put description of what we're doing in top center
///
protected override string CenterHeader()
- {
- return taskDescription;
- }
+ => taskDescription;
// IUser stuff for managing the progress bar and message box
@@ -72,7 +67,8 @@ public override bool RaiseYesNoDialog(string question)
{
// Show the popup at the top of the screen
// to overwrite the progress bar instead of the messages
- ConsoleMessageDialog d = new ConsoleMessageDialog(
+ var d = new ConsoleMessageDialog(
+ theme,
// The installer's questions include embedded newlines for spacing in CmdLine
question.Trim(),
new List() {
@@ -83,22 +79,22 @@ public override bool RaiseYesNoDialog(string question)
TextAlign.Center,
-Console.WindowHeight / 2
);
- d.AddBinding(Keys.Y, (object sender, ConsoleTheme theme) => {
+ d.AddBinding(Keys.Y, (object sender) => {
d.PressButton(0);
return false;
});
- d.AddBinding(Keys.N, (object sender, ConsoleTheme theme) => {
+ d.AddBinding(Keys.N, (object sender) => {
d.PressButton(1);
return false;
});
// Scroll messages
d.AddTip(Properties.Resources.CursorKeys, Properties.Resources.ScrollMessages);
- messages.AddScrollBindings(d, true);
+ messages.AddScrollBindings(d, theme, true);
- bool val = d.Run(yesNoTheme) == 0;
- DrawBackground(yesNoTheme);
- Draw(yesNoTheme);
+ bool val = d.Run() == 0;
+ DrawBackground();
+ Draw();
return val;
}
@@ -126,8 +122,6 @@ protected override void Progress(string message, int percent)
private readonly ConsoleProgressBar progress;
private readonly ConsoleTextBox messages;
- private ConsoleTheme yesNoTheme;
-
private string topMessage = "";
private readonly string taskDescription;
private double percent = 0;
diff --git a/ConsoleUI/RepoAddScreen.cs b/ConsoleUI/RepoAddScreen.cs
index 90e79c5c14..ab83a15b26 100644
--- a/ConsoleUI/RepoAddScreen.cs
+++ b/ConsoleUI/RepoAddScreen.cs
@@ -1,5 +1,5 @@
using System.Collections.Generic;
-
+using CKAN.ConsoleUI.Toolkit;
using CKAN.Games;
namespace CKAN.ConsoleUI {
@@ -12,10 +12,11 @@ public class RepoAddScreen : RepoScreen {
///
/// Construct the screen
///
+ /// The visual theme to use to draw the dialog
/// Game from which to get repos
/// Collection of Repository objects
- public RepoAddScreen(IGame game, SortedDictionary reps)
- : base(game, reps, "", "") { }
+ public RepoAddScreen(ConsoleTheme theme, IGame game, SortedDictionary reps)
+ : base(theme, game, reps, "", "") { }
///
/// Check whether the fields are valid
diff --git a/ConsoleUI/RepoEditScreen.cs b/ConsoleUI/RepoEditScreen.cs
index 32349fab2d..288f199b43 100644
--- a/ConsoleUI/RepoEditScreen.cs
+++ b/ConsoleUI/RepoEditScreen.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using CKAN.ConsoleUI.Toolkit;
using CKAN.Games;
namespace CKAN.ConsoleUI {
@@ -12,11 +13,12 @@ public class RepoEditScreen : RepoScreen {
///
/// Construct the Screen
///
+ /// The visual theme to use to draw the dialog
/// Game from which to get repos
/// Collection of Repository objects
/// The object to edit
- public RepoEditScreen(IGame game, SortedDictionary reps, Repository repo)
- : base(game, reps, repo.name, repo.uri.ToString())
+ public RepoEditScreen(ConsoleTheme theme, IGame game, SortedDictionary reps, Repository repo)
+ : base(theme, game, reps, repo.name, repo.uri.ToString())
{
repository = repo;
}
diff --git a/ConsoleUI/RepoScreen.cs b/ConsoleUI/RepoScreen.cs
index 601dbb7bd3..5739423d37 100644
--- a/ConsoleUI/RepoScreen.cs
+++ b/ConsoleUI/RepoScreen.cs
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
-
+using System.Linq;
using CKAN.ConsoleUI.Toolkit;
using CKAN.Games;
@@ -14,14 +14,15 @@ public abstract class RepoScreen : ConsoleScreen {
///
/// Construct the screens
///
+ /// The visual theme to use to draw the dialog
/// Game from which to get repos
/// Collection of Repository objects
/// Initial value of the Name field
/// Iniital value of the URL field
- protected RepoScreen(IGame game, SortedDictionary reps, string initName, string initUrl) : base()
+ protected RepoScreen(ConsoleTheme theme, IGame game, SortedDictionary reps, string initName, string initUrl)
+ : base(theme)
{
editList = reps;
- defaultRepos = RepositoryList.DefaultRepositories(game);
name = new ConsoleField(labelWidth, nameRow, -1, initName) {
GhostText = () => Properties.Resources.RepoNameGhostText
@@ -36,7 +37,7 @@ protected RepoScreen(IGame game, SortedDictionary reps, stri
AddObject(url);
AddTip("F2", Properties.Resources.Accept);
- AddBinding(Keys.F2, (object sender, ConsoleTheme theme) => {
+ AddBinding(Keys.F2, (object sender) => {
if (Valid()) {
Save();
return false;
@@ -46,46 +47,40 @@ protected RepoScreen(IGame game, SortedDictionary reps, stri
});
AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
- AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
- return false;
- });
+ AddBinding(Keys.Escape, (object sender) => false);
// mainMenu = list of default options
- if (defaultRepos.repositories != null && defaultRepos.repositories.Length > 0) {
- List opts = new List();
- foreach (Repository r in defaultRepos.repositories) {
- // This variable will be remembered correctly in our lambdas later
- Repository repo = r;
- opts.Add(new ConsoleMenuOption(
- repo.name, "", string.Format(Properties.Resources.RepoImportTip, repo.name),
- true, (ConsoleTheme theme) => {
- name.Value = repo.name;
- name.Position = name.Value.Length;
- url.Value = repo.uri.ToString();
- url.Position = url.Value.Length;
- return true;
- }
- ));
- }
- mainMenu = new ConsolePopupMenu(opts);
- }
+ mainMenu = (RepositoryList.DefaultRepositories(game) is RepositoryList repoList)
+ ? new ConsolePopupMenu(
+ repoList.repositories
+ .Select(r => new ConsoleMenuOption(r.name,
+ "",
+ string.Format(Properties.Resources.RepoImportTip,
+ r.name),
+ true,
+ () => {
+ name.Value = r.name;
+ name.Position = name.Value.Length;
+ url.Value = r.uri.ToString();
+ url.Position = url.Value.Length;
+ return true;
+ }))
+ .OfType()
+ .ToList())
+ : null;
}
///
/// Put CKAN 1.25.5 in top left corner
///
protected override string LeftHeader()
- {
- return $"{Meta.GetProductName()} {Meta.GetVersion()}";
- }
+ => $"{Meta.GetProductName()} {Meta.GetVersion()}";
///
/// Put description in top center
///
protected override string CenterHeader()
- {
- return Properties.Resources.RepoTitle;
- }
+ => Properties.Resources.RepoTitle;
///
/// Report whether the fields are Valid
@@ -153,12 +148,9 @@ protected bool urlValid()
///
protected SortedDictionary editList;
- private RepositoryList defaultRepos;
-
private int labelWidth => Math.Max(8, Math.Max(
Properties.Resources.RepoNameLabel.Length,
- Properties.Resources.RepoURLLabel.Length
- ));
+ Properties.Resources.RepoURLLabel.Length));
private const int nameRow = 3;
private const int urlRow = 5;
}
diff --git a/ConsoleUI/SingleAssemblyResourceManager.cs b/ConsoleUI/SingleAssemblyResourceManager.cs
index ecdf62c50d..9fa6456806 100644
--- a/ConsoleUI/SingleAssemblyResourceManager.cs
+++ b/ConsoleUI/SingleAssemblyResourceManager.cs
@@ -1,4 +1,3 @@
-using System.IO;
using System.Globalization;
using System.Resources;
using System.Reflection;
@@ -31,16 +30,13 @@ public SingleAssemblyResourceManager(string basename, Assembly assembly)
/// Set to false to avoid loading if not already cached
/// Just gets passed to base class implementation
///
- protected override ResourceSet InternalGetResourceSet(CultureInfo culture,
+ protected override ResourceSet? InternalGetResourceSet(CultureInfo culture,
bool createIfNotExists, bool tryParents)
{
- if (!myResourceSets.TryGetValue(culture, out ResourceSet rs) && createIfNotExists)
+ if (!myResourceSets.TryGetValue(culture, out ResourceSet? rs) && createIfNotExists && MainAssembly != null)
{
// Lazy-load default language (without caring about duplicate assignment in race conditions, no harm done)
- if (neutralResourcesCulture == null)
- {
- neutralResourcesCulture = GetNeutralResourcesLanguage(MainAssembly);
- }
+ neutralResourcesCulture ??= GetNeutralResourcesLanguage(MainAssembly);
// If we're asking for the default language, then ask for the
// invariant (non-specific) resources.
@@ -50,7 +46,7 @@ protected override ResourceSet InternalGetResourceSet(CultureInfo culture,
}
string resourceFileName = GetResourceFileName(culture);
- Stream store = MainAssembly.GetManifestResourceStream(resourceFileName);
+ var store = MainAssembly.GetManifestResourceStream(resourceFileName);
// If we found the appropriate resources in the local assembly
if (store != null)
@@ -67,7 +63,7 @@ protected override ResourceSet InternalGetResourceSet(CultureInfo culture,
return rs;
}
- private CultureInfo neutralResourcesCulture;
+ private CultureInfo? neutralResourcesCulture;
private readonly Dictionary myResourceSets = new Dictionary();
}
}
diff --git a/ConsoleUI/SplashScreen.cs b/ConsoleUI/SplashScreen.cs
index 4e56aa11ea..d76f0a01cb 100644
--- a/ConsoleUI/SplashScreen.cs
+++ b/ConsoleUI/SplashScreen.cs
@@ -28,7 +28,7 @@ public SplashScreen(GameInstanceManager mgr, RepositoryDataManager repoData)
public bool Run(ConsoleTheme theme)
{
// If there's a default instance, try to get the lock for it.
- GameInstance ksp = manager.CurrentInstance ?? manager.GetPreferredInstance();
+ GameInstance? ksp = manager.CurrentInstance ?? manager.GetPreferredInstance();
if (ksp != null
&& !GameInstanceListScreen.TryGetInstance(theme, ksp, repoData,
(ConsoleTheme th) => Draw(th, false),
diff --git a/ConsoleUI/Toolkit/ConsoleChoiceDialog.cs b/ConsoleUI/Toolkit/ConsoleChoiceDialog.cs
index 28c5170bd9..6c38d78e7d 100644
--- a/ConsoleUI/Toolkit/ConsoleChoiceDialog.cs
+++ b/ConsoleUI/Toolkit/ConsoleChoiceDialog.cs
@@ -12,13 +12,14 @@ public class ConsoleChoiceDialog : ConsoleDialog {
///
/// Initialize the Dialog
///
+ /// The visual theme to use to draw the dialog
/// Message to show
/// Text for column header of list box
/// List of objects to put in the list box
/// Function to generate text for each option
/// Optional function to sort the rows
- public ConsoleChoiceDialog(string m, string hdr, List c, Func renderer, Comparison comparer = null)
- : base()
+ public ConsoleChoiceDialog(ConsoleTheme theme, string m, string hdr, List c, Func renderer, Comparison? comparer = null)
+ : base(theme)
{
int l = GetLeft(),
r = GetRight();
@@ -34,7 +35,7 @@ public ConsoleChoiceDialog(string m, string hdr, List c, Func c, Func>() {
- new ConsoleListBoxColumn() {
- Header = hdr,
- Width = null,
- Renderer = renderer,
- Comparer = comparer
- }
+ new ConsoleListBoxColumn(hdr, renderer, comparer, null)
},
0, 0, ListSortDirection.Ascending
);
choices.AddTip(Properties.Resources.Enter, Properties.Resources.Accept);
- choices.AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => {
+ choices.AddBinding(Keys.Enter, (object sender) => {
return false;
});
choices.AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
- choices.AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
+ choices.AddBinding(Keys.Escape, (object sender) => {
cancelled = true;
return false;
});
@@ -76,14 +72,13 @@ public ConsoleChoiceDialog(string m, string hdr, List c, Func
/// Display the dialog and handle its interaction
///
- /// The visual theme to use to draw the dialog
/// Function to control the dialog, default is normal user interaction
///
/// Row user selected
///
- public new ChoiceT Run(ConsoleTheme theme, Action process = null)
+ public new ChoiceT? Run(Action? process = null)
{
- base.Run(theme, process);
+ base.Run(process);
return cancelled ? default : choices.Selection;
}
diff --git a/ConsoleUI/Toolkit/ConsoleDialog.cs b/ConsoleUI/Toolkit/ConsoleDialog.cs
index 6bbd02b539..914c77d28a 100644
--- a/ConsoleUI/Toolkit/ConsoleDialog.cs
+++ b/ConsoleUI/Toolkit/ConsoleDialog.cs
@@ -11,7 +11,9 @@ public abstract class ConsoleDialog : ScreenContainer {
/// Initialize the dialog.
/// By default sets size and position to middle 50%.
///
- protected ConsoleDialog()
+ /// The visual theme to use to draw the dialog
+ protected ConsoleDialog(ConsoleTheme theme)
+ : base(theme)
{
left = Console.WindowWidth / 4;
top = Console.WindowHeight / 4;
@@ -63,19 +65,19 @@ public static void DrawShadow(ConsoleTheme theme, int l, int t, int r, int b)
///
/// X coordinate of left edge of dialog
///
- protected int GetLeft() { return Formatting.ConvertCoord(left, Console.WindowWidth); }
+ protected int GetLeft() => Formatting.ConvertCoord(left, Console.WindowWidth);
///
/// Y coordinate of top edge of dialog
///
- protected int GetTop() { return Formatting.ConvertCoord(top, Console.WindowHeight); }
+ protected int GetTop() => Formatting.ConvertCoord(top, Console.WindowHeight);
///
/// X coordinate of right edge of dialog
///
- protected int GetRight() { return Formatting.ConvertCoord(right, Console.WindowWidth); }
+ protected int GetRight() => Formatting.ConvertCoord(right, Console.WindowWidth);
///
/// Y coordinate of bottom edge of dialog
///
- protected int GetBottom() { return Formatting.ConvertCoord(bottom, Console.WindowHeight); }
+ protected int GetBottom() => Formatting.ConvertCoord(bottom, Console.WindowHeight);
///
/// Set position of dialog
@@ -95,7 +97,7 @@ protected void SetDimensions(int l, int t, int r, int b)
///
/// Draw the outline of the dialog and clear the footer
///
- protected override void DrawBackground(ConsoleTheme theme)
+ protected override void DrawBackground()
{
int w = GetRight() - GetLeft() + 1;
string fullHorizLineDouble = new string(Symbols.horizLineDouble, w - 2);
diff --git a/ConsoleUI/Toolkit/ConsoleDoubleFrame.cs b/ConsoleUI/Toolkit/ConsoleDoubleFrame.cs
index f77d3e8e94..b0993479ba 100644
--- a/ConsoleUI/Toolkit/ConsoleDoubleFrame.cs
+++ b/ConsoleUI/Toolkit/ConsoleDoubleFrame.cs
@@ -76,7 +76,7 @@ private void writeTitleRow(string title, int w)
if (leftSidePad < 0 || rightSidePad < 0) {
leftSidePad = 0;
rightSidePad = 0;
- title = title.Substring(0, w - 4);
+ title = title[..(w - 4)];
}
Console.Write(new string(doubleBorder ? Symbols.horizLineDouble : Symbols.horizLine, leftSidePad));
Console.Write($" {title} ");
diff --git a/ConsoleUI/Toolkit/ConsoleField.cs b/ConsoleUI/Toolkit/ConsoleField.cs
index 885030eb8e..109b4983da 100644
--- a/ConsoleUI/Toolkit/ConsoleField.cs
+++ b/ConsoleUI/Toolkit/ConsoleField.cs
@@ -45,7 +45,7 @@ public ConsoleField(int l, int t, int r, string val = "")
///
/// Event to notify that the text has changed
///
- public event ChangeListener OnChange;
+ public event ChangeListener? OnChange;
private void Changed()
{
OnChange?.Invoke(this, Value);
@@ -89,7 +89,7 @@ public override void Draw(ConsoleTheme theme, bool focused)
Console.ForegroundColor = focused
? theme.FieldFocusedFg
: theme.FieldBlurredFg;
- Console.Write(FormatExactWidth(Value.Substring(leftPos), w));
+ Console.Write(FormatExactWidth(Value[leftPos..], w));
}
}
@@ -110,11 +110,11 @@ public override void OnKeyPress(ConsoleKeyInfo k)
if (!k.Modifiers.HasFlag(ConsoleModifiers.Control)) {
if (Position > 0) {
--Position;
- Value = Value.Substring(0, Position) + Value.Substring(Position + 1);
+ Value = Value[..Position] + Value[(Position + 1)..];
Changed();
}
} else if (!string.IsNullOrEmpty(Value)) {
- Value = Value.Substring(Position);
+ Value = Value[Position..];
Position = 0;
Changed();
}
@@ -122,8 +122,8 @@ public override void OnKeyPress(ConsoleKeyInfo k)
case ConsoleKey.Delete:
if (Position < Value.Length) {
Value = k.Modifiers.HasFlag(ConsoleModifiers.Control)
- ? Value.Substring(0, Position)
- : Value.Substring(0, Position) + Value.Substring(Position + 1);
+ ? Value[..Position]
+ : Value[..Position] + Value[(Position + 1)..];
Changed();
}
break;
@@ -155,7 +155,7 @@ public override void OnKeyPress(ConsoleKeyInfo k)
default:
if (!char.IsControl(k.KeyChar)) {
if (Position < Value.Length) {
- Value = Value.Substring(0, Position) + k.KeyChar + Value.Substring(Position);
+ Value = Value[..Position] + k.KeyChar + Value[Position..];
} else {
Value += k.KeyChar;
}
diff --git a/ConsoleUI/Toolkit/ConsoleFileMultiSelectDialog.cs b/ConsoleUI/Toolkit/ConsoleFileMultiSelectDialog.cs
index 613dc99583..20ed445d17 100644
--- a/ConsoleUI/Toolkit/ConsoleFileMultiSelectDialog.cs
+++ b/ConsoleUI/Toolkit/ConsoleFileMultiSelectDialog.cs
@@ -2,6 +2,7 @@
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
namespace CKAN.ConsoleUI.Toolkit {
@@ -13,17 +14,19 @@ public class ConsoleFileMultiSelectDialog : ConsoleDialog {
///
/// Initialize the popup.
///
+ /// The visual theme to use to draw the dialog
/// String to be shown in the top center of the popup
/// Path of directory to start in
/// Glob-style wildcard string for matching files to show
/// Header for the column with checkmarks for selected files
/// Description of the F9 action to accept selections
- public ConsoleFileMultiSelectDialog(string title,
+ public ConsoleFileMultiSelectDialog(ConsoleTheme theme,
+ string title,
string startPath,
string filPat,
string toggleHeader,
string acceptTip)
- : base()
+ : base(theme)
{
CenterHeader = () => title;
curDir = new DirectoryInfo(startPath);
@@ -63,57 +66,51 @@ public ConsoleFileMultiSelectDialog(string title,
left + 2, top + 4, right - 2, bottom - 2,
getFileList(),
new List>() {
- new ConsoleListBoxColumn() {
- Header = toggleHeader,
- Width = 8,
- Renderer = getRowSymbol
- }, new ConsoleListBoxColumn() {
- Header = Properties.Resources.FileSelectNameHeader,
- Width = null,
- Renderer = getRowName,
- Comparer = compareNames
- }, new ConsoleListBoxColumn() {
- Header = Properties.Resources.FileSelectSizeHeader,
- // Longest: "1023.1 KB"
- Width = 9,
- Renderer = (FileSystemInfo fi) => getLength(fi),
- Comparer = (a, b) => {
- FileInfo fb = b as FileInfo;
- return !(a is FileInfo fa)
+ new ConsoleListBoxColumn(
+ toggleHeader, getRowSymbol, null, 8),
+ new ConsoleListBoxColumn(
+ Properties.Resources.FileSelectNameHeader,
+ getRowName, compareNames, null),
+ new ConsoleListBoxColumn(
+ Properties.Resources.FileSelectSizeHeader,
+ (FileSystemInfo fi) => getLength(fi),
+ (a, b) => {
+ var fb = b as FileInfo;
+ return a is not FileInfo fa
? (fb == null ? 0 : -1)
: (fb == null ? 1 : fa.Length.CompareTo(fb.Length));
- }
- }, new ConsoleListBoxColumn() {
- Header = Properties.Resources.FileSelectTimestampHeader,
- Width = 10,
- Renderer = (FileSystemInfo fi) => fi.LastWriteTime.ToString("yyyy-MM-dd"),
- Comparer = (a, b) => a.LastWriteTime.CompareTo(b.LastWriteTime)
- }
+ },
+ 9),
+ new ConsoleListBoxColumn(
+ Properties.Resources.FileSelectTimestampHeader,
+ (FileSystemInfo fi) => fi.LastWriteTime.ToString("yyyy-MM-dd"),
+ (a, b) => a.LastWriteTime.CompareTo(b.LastWriteTime),
+ 10)
},
1, 1, ListSortDirection.Ascending
);
AddObject(fileList);
AddTip(Properties.Resources.Esc, Properties.Resources.Cancel);
- AddBinding(Keys.Escape, (object sender, ConsoleTheme theme) => {
+ AddBinding(Keys.Escape, (object sender) => {
chosenFiles.Clear();
return false;
});
AddTip("F10", Properties.Resources.Sort);
- AddBinding(Keys.F10, (object sender, ConsoleTheme theme) => {
+ AddBinding(Keys.F10, (object sender) => {
fileList.SortMenu().Run(theme, right - 2, top + 2);
- DrawBackground(theme);
+ DrawBackground();
return true;
});
AddTip(Properties.Resources.Enter, Properties.Resources.FileSelectChangeDirectory, () => fileList.Selection != null && isDir(fileList.Selection));
AddTip(Properties.Resources.Enter, Properties.Resources.FileSelectSelect, () => fileList.Selection != null && !isDir(fileList.Selection));
- AddBinding(Keys.Enter, (object sender, ConsoleTheme theme) => selectRow());
- AddBinding(Keys.Space, (object sender, ConsoleTheme theme) => selectRow());
+ AddBinding(Keys.Enter, (object sender) => selectRow());
+ AddBinding(Keys.Space, (object sender) => selectRow());
AddTip($"{Properties.Resources.Ctrl}+A", Properties.Resources.SelectAll);
- AddBinding(Keys.CtrlA, (object sender, ConsoleTheme theme) => {
+ AddBinding(Keys.CtrlA, (object sender) => {
foreach (FileSystemInfo fi in contents) {
if (!isDir(fi)) {
if (fi is FileInfo file)
@@ -126,7 +123,7 @@ public ConsoleFileMultiSelectDialog(string title,
});
AddTip($"{Properties.Resources.Ctrl}+D", Properties.Resources.DeselectAll, () => chosenFiles.Count > 0);
- AddBinding(Keys.CtrlD, (object sender, ConsoleTheme theme) => {
+ AddBinding(Keys.CtrlD, (object sender) => {
if (chosenFiles.Count > 0) {
chosenFiles.Clear();
}
@@ -134,12 +131,12 @@ public ConsoleFileMultiSelectDialog(string title,
});
AddTip("F9", acceptTip, () => chosenFiles.Count > 0);
- AddBinding(Keys.F9, (object sender, ConsoleTheme theme) => false);
+ AddBinding(Keys.F9, (object sender) => false);
}
private bool selectRow()
{
- if (isDir(fileList.Selection)) {
+ if (fileList.Selection != null && isDir(fileList.Selection)) {
if (fileList.Selection is DirectoryInfo di)
{
curDir = di;
@@ -176,17 +173,17 @@ private void pathFieldChanged(ConsoleField sender, string newValue)
///
/// Display the dialog and handle its interaction
///
- /// The visual theme to use to draw the dialog
/// Function to control the dialog, default is normal user interaction
///
/// Files user selected
///
- public new HashSet Run(ConsoleTheme theme, Action process = null)
+ public new HashSet Run(Action? process = null)
{
- base.Run(theme, process);
+ base.Run(process);
return chosenFiles;
}
+ [MemberNotNull(nameof(contents))]
private IList getFileList()
{
contents = new List();
@@ -210,7 +207,7 @@ private static bool isDir(FileSystemInfo fi)
///
/// True if they're the same file/directory, false otherwise.
///
- private static bool pathEquals(FileSystemInfo a, FileSystemInfo b)
+ private static bool pathEquals(FileSystemInfo? a, FileSystemInfo? b)
{
if (a == null || b == null) {
return false;
@@ -247,7 +244,9 @@ private long totalChosenSize()
}
private string getRowSymbol(FileSystemInfo fi)
- => !isDir(fi) && chosenFiles.Contains(fi as FileInfo)
+ => !isDir(fi)
+ && fi is FileInfo file
+ && chosenFiles.Contains(file)
? chosen
: "";
diff --git a/ConsoleUI/Toolkit/ConsoleFrame.cs b/ConsoleUI/Toolkit/ConsoleFrame.cs
index 15a9c15c02..c13c9716e8 100644
--- a/ConsoleUI/Toolkit/ConsoleFrame.cs
+++ b/ConsoleUI/Toolkit/ConsoleFrame.cs
@@ -47,7 +47,7 @@ public override void Draw(ConsoleTheme theme, bool focused)
if (topLeftSidePad < 0 || topRightSidePad < 0) {
topLeftSidePad = 0;
topRightSidePad = 0;
- title = title.Substring(0, w - 4);
+ title = title[..(w - 4)];
}
Console.Write(new string(doubleBorder ? Symbols.horizLineDouble : Symbols.horizLine, topLeftSidePad));
Console.Write($" {title} ");
diff --git a/ConsoleUI/Toolkit/ConsoleLabel.cs b/ConsoleUI/Toolkit/ConsoleLabel.cs
index ebbb9e95ac..f4131f6f12 100644
--- a/ConsoleUI/Toolkit/ConsoleLabel.cs
+++ b/ConsoleUI/Toolkit/ConsoleLabel.cs
@@ -16,7 +16,12 @@ public class ConsoleLabel : ScreenObject {
/// Function returning the text to show in the label
/// Function returning the background color for the label
/// Function returning the foreground color for the label
- public ConsoleLabel(int l, int t, int r, Func lf, Func bgFunc = null, Func fgFunc = null)
+ public ConsoleLabel(int l,
+ int t,
+ int r,
+ Func lf,
+ Func? bgFunc = null,
+ Func? fgFunc = null)
: base(l, t, r, t)
{
labelFunc = lf;
@@ -48,8 +53,8 @@ public override void Draw(ConsoleTheme theme, bool focused)
public override bool Focusable() { return false; }
private readonly Func labelFunc;
- private readonly Func getBgColor;
- private readonly Func getFgColor;
+ private readonly Func? getBgColor;
+ private readonly Func? getFgColor;
}
}
diff --git a/ConsoleUI/Toolkit/ConsoleListBox.cs b/ConsoleUI/Toolkit/ConsoleListBox.cs
index c19c628dce..347fb99673 100644
--- a/ConsoleUI/Toolkit/ConsoleListBox.cs
+++ b/ConsoleUI/Toolkit/ConsoleListBox.cs
@@ -3,6 +3,7 @@
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
+using System.Diagnostics.CodeAnalysis;
namespace CKAN.ConsoleUI.Toolkit {
@@ -30,7 +31,7 @@ public ConsoleListBox(int l, int t, int r, int b,
int dfltSortCol,
int initialSortCol = 0,
ListSortDirection initialSortDir = ListSortDirection.Ascending,
- Func filt = null)
+ Func? filt = null)
: base(l, t, r, b)
{
data = dataList;
@@ -47,7 +48,7 @@ public ConsoleListBox(int l, int t, int r, int b,
///
/// Fired when the user changes the selection with the arrow keys
///
- public event Action SelectionChanged;
+ public event Action? SelectionChanged;
///
/// Set which column to sort by
@@ -88,15 +89,15 @@ public string FilterString {
///