diff --git a/CHANGELOG.md b/CHANGELOG.md index c4af91e4b..32b233d95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,6 +59,7 @@ All notable changes to this project will be documented in this file. - [Multiple] Stop checking multiple hashes (#4135 by: HebaruSan) - [Core] Fix max version column for wildcard compat (#4142 by: HebaruSan) - [Multiple] Refactor ZIP importing (#4153 by: HebaruSan) +- [GUI] Work around OpenFileDialog always showing all shortcuts despite filters (#4168 by: HebaruSan) ### Internal diff --git a/Core/CkanTransaction.cs b/Core/CkanTransaction.cs index cafdabfd4..f9d4ae504 100644 --- a/Core/CkanTransaction.cs +++ b/Core/CkanTransaction.cs @@ -26,7 +26,7 @@ public static TransactionScope CreateTransactionScope() } // System.ArgumentOutOfRangeException : Time-out interval must be less than 2^32-2. (Parameter 'dueTime') - private const double timeoutMs = Int32.MaxValue; + private const double timeoutMs = int.MaxValue; private static readonly TimeSpan maxCoretimeout = TimeSpan.FromMilliseconds(timeoutMs); private static readonly TransactionOptions transOpts = new TransactionOptions() diff --git a/Core/Configuration/JsonConfiguration.cs b/Core/Configuration/JsonConfiguration.cs index db844f080..0ba771f42 100644 --- a/Core/Configuration/JsonConfiguration.cs +++ b/Core/Configuration/JsonConfiguration.cs @@ -75,7 +75,7 @@ public JsonConfiguration(string newConfig = null) // If you have multiple instances of CKAN running at the same time, each will // believe that their copy of the config file in memory is authoritative, so // changes made by one copy will not be respected by the other. - private string configFile = defaultConfigFile; + private readonly string configFile = defaultConfigFile; private Config config = null; public string DownloadCacheDir diff --git a/GUI/Dialogs/ManageGameInstancesDialog.cs b/GUI/Dialogs/ManageGameInstancesDialog.cs index 25a6b8465..4404220ca 100644 --- a/GUI/Dialogs/ManageGameInstancesDialog.cs +++ b/GUI/Dialogs/ManageGameInstancesDialog.cs @@ -18,42 +18,24 @@ namespace CKAN.GUI #endif public partial class ManageGameInstancesDialog : Form { - private static GameInstanceManager manager => Main.Instance.Manager; - private readonly IUser _user; - private RenameInstanceDialog _renameInstanceDialog; - private readonly OpenFileDialog _instanceDialog = new OpenFileDialog() - { - AddExtension = false, - CheckFileExists = false, - CheckPathExists = false, - InitialDirectory = Environment.CurrentDirectory, - Filter = GameFolderFilter(manager), - Multiselect = false - }; - - /// - /// Generate filter string for OpenFileDialog - /// - /// Game instance manager that can tell us about the build ID files - /// - /// "Build metadata files (buildID.txt;buildID64.txt)|buildID.txt;buildID64.txt" - /// - public static string GameFolderFilter(GameInstanceManager mgr) - => Properties.Resources.GameProgramFileDescription - + "|" + string.Join(";", mgr.AllInstanceAnchorFiles); - - public bool HasSelections => GameInstancesListView.SelectedItems.Count > 0; - /// /// Initialize the game instance selection window /// + /// Game instance manager object to provide our game instances /// true to center the window on the screen, false to center it on the parent - public ManageGameInstancesDialog(bool centerScreen, IUser user) + /// IUser object reference for raising dialogs + public ManageGameInstancesDialog(GameInstanceManager mgr, + bool centerScreen, + IUser user) { - _user = user; + manager = mgr; + this.user = user; InitializeComponent(); DialogResult = DialogResult.Cancel; + instanceDialog.Filter = GameFolderFilter(manager); + instanceDialog.FileOk += InstanceFileOK; + if (centerScreen) { StartPosition = FormStartPosition.CenterScreen; @@ -101,6 +83,19 @@ public void UpdateInstancesList() GameInstancesListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize); } + /// + /// Generate filter string for OpenFileDialog + /// + /// Game instance manager that can tell us about the build ID files + /// + /// "Build metadata files (buildID.txt;buildID64.txt)|buildID.txt;buildID64.txt" + /// + public static string GameFolderFilter(GameInstanceManager mgr) + => Properties.Resources.GameProgramFileDescription + + "|" + string.Join(";", mgr.AllInstanceAnchorFiles); + + public bool HasSelections => GameInstancesListView.SelectedItems.Count > 0; + private void AddOrRemoveColumn(ListView listView, ColumnHeader column, bool condition, int index) { if (condition && !listView.Columns.Contains(column)) @@ -157,22 +152,20 @@ protected override void OnHelpButtonClicked(CancelEventArgs evt) } private static string FormatVersion(GameVersion v) - { - return v == null + => v == null ? Properties.Resources.CompatibleGameVersionsDialogNone // The BUILD component is not useful visually : new GameVersion(v.Major, v.Minor, v.Patch).ToString(); - } private void AddToCKANMenuItem_Click(object sender, EventArgs e) { - if (_instanceDialog.ShowDialog(this) != DialogResult.OK - || !File.Exists(_instanceDialog.FileName)) + if (instanceDialog.ShowDialog(this) != DialogResult.OK + || !File.Exists(instanceDialog.FileName)) { return; } - var path = Path.GetDirectoryName(_instanceDialog.FileName); + var path = Path.GetDirectoryName(instanceDialog.FileName); try { var instanceName = Path.GetFileName(path); @@ -181,17 +174,17 @@ private void AddToCKANMenuItem_Click(object sender, EventArgs e) instanceName = path; } instanceName = manager.GetNextValidInstanceName(instanceName); - manager.AddInstance(path, instanceName, _user); + manager.AddInstance(path, instanceName, user); UpdateInstancesList(); } catch (NotKSPDirKraken k) { - _user.RaiseError(Properties.Resources.ManageGameInstancesNotValid, + user.RaiseError(Properties.Resources.ManageGameInstancesNotValid, new object[] { k.path }); } catch (Exception exc) { - _user.RaiseError(exc.Message); + user.RaiseError(exc.Message); } } @@ -213,7 +206,7 @@ private void CloneGameInstanceMenuItem_Click(object sender, EventArgs e) { var old_instance = manager.CurrentInstance; - var result = new CloneGameInstanceDialog(manager, _user, (string)GameInstancesListView.SelectedItems[0].Tag).ShowDialog(this); + var result = new CloneGameInstanceDialog(manager, user, (string)GameInstancesListView.SelectedItems[0].Tag).ShowDialog(this); if (result == DialogResult.OK && !Equals(old_instance, manager.CurrentInstance)) { DialogResult = DialogResult.OK; @@ -246,7 +239,7 @@ private void UseSelectedInstance() } catch (NotKSPDirKraken k) { - _user.RaiseError(Properties.Resources.ManageGameInstancesNotValid, k.path); + user.RaiseError(Properties.Resources.ManageGameInstancesNotValid, k.path); } } } @@ -270,7 +263,7 @@ private void SetAsDefaultCheckbox_Click(object sender, EventArgs e) } catch (NotKSPDirKraken k) { - _user.RaiseError(Properties.Resources.ManageGameInstancesNotValid, k.path); + user.RaiseError(Properties.Resources.ManageGameInstancesNotValid, k.path); } } } @@ -321,7 +314,7 @@ private void OpenDirectoryMenuItem_Click(object sender, EventArgs e) if (!Directory.Exists(path)) { - _user.RaiseError(Properties.Resources.ManageGameInstancesDirectoryDeleted, path); + user.RaiseError(Properties.Resources.ManageGameInstancesDirectoryDeleted, path); return; } @@ -333,14 +326,14 @@ private void RenameButton_Click(object sender, EventArgs e) var instance = (string)GameInstancesListView.SelectedItems[0].Tag; // show the dialog, and only continue if the user selected "OK" - _renameInstanceDialog = new RenameInstanceDialog(); - if (_renameInstanceDialog.ShowRenameInstanceDialog(instance) != DialogResult.OK) + var renameInstanceDialog = new RenameInstanceDialog(); + if (renameInstanceDialog.ShowRenameInstanceDialog(instance) != DialogResult.OK) { return; } // proceed with instance rename - manager.RenameInstance(instance, _renameInstanceDialog.GetResult()); + manager.RenameInstance(instance, renameInstanceDialog.GetResult()); UpdateInstancesList(); } @@ -359,5 +352,39 @@ private void UpdateButtonState() ForgetButton.Enabled = HasSelections && (string)GameInstancesListView.SelectedItems[0].Tag != manager.CurrentInstance?.Name; ImportFromSteamMenuItem.Enabled = manager.SteamLibrary.Games.Length > 0; } + + private readonly GameInstanceManager manager; + + private readonly IUser user; + + private readonly OpenFileDialog instanceDialog = new OpenFileDialog() + { + AddExtension = false, + CheckFileExists = false, + CheckPathExists = false, + DereferenceLinks = false, + InitialDirectory = Environment.CurrentDirectory, + Multiselect = false, + }; + + private void InstanceFileOK(object sender, CancelEventArgs e) + { + if (sender is OpenFileDialog dlg) + { + // OpenFileDialog always shows shortcuts (!!!!!), + // so we have to re-enforce the filter ourselves + var chosen = Path.GetFileName(dlg.FileName); + var allowed = manager.AllInstanceAnchorFiles; + if (!allowed.Contains(chosen)) + { + e.Cancel = true; + user.RaiseError(Properties.Resources.ManageGameInstancesInvalidFileSelected, + chosen, + string.Join(Environment.NewLine, + allowed.OrderBy(f => f) + .Select(f => $" - {f}"))); + } + } + } } } diff --git a/GUI/Main/Main.cs b/GUI/Main/Main.cs index 3371279a7..d5dfc211f 100644 --- a/GUI/Main/Main.cs +++ b/GUI/Main/Main.cs @@ -326,7 +326,7 @@ private bool InstancePromptAtStart() bool gotInstance = false; Util.Invoke(this, () => { - var result = new ManageGameInstancesDialog(!actuallyVisible, currentUser).ShowDialog(this); + var result = new ManageGameInstancesDialog(Manager, !actuallyVisible, currentUser).ShowDialog(this); gotInstance = result == DialogResult.OK; }); return gotInstance; @@ -335,7 +335,7 @@ private bool InstancePromptAtStart() private void manageGameInstancesMenuItem_Click(object sender, EventArgs e) { var old_instance = CurrentInstance; - var result = new ManageGameInstancesDialog(!actuallyVisible, currentUser).ShowDialog(this); + var result = new ManageGameInstancesDialog(Manager, !actuallyVisible, currentUser).ShowDialog(this); if (result == DialogResult.OK && !Equals(old_instance, CurrentInstance)) { for (bool done = false; !done;) diff --git a/GUI/Model/ModList.cs b/GUI/Model/ModList.cs index 52420350d..f99abbc0e 100644 --- a/GUI/Model/ModList.cs +++ b/GUI/Model/ModList.cs @@ -193,7 +193,7 @@ public Tuple, Dictionary, List, Dictionary, ListCompatible versions of current instance /// Sequence of InstalledModules after the changes are applied, not including dependencies private IEnumerable InstalledAfterChanges( - IRegistryQuerier registry, HashSet changeSet, GameVersionCriteria crit) + IRegistryQuerier registry, HashSet changeSet) { var removingIdents = changeSet .Where(ch => ch.ChangeType != GUIModChangeType.Install) diff --git a/GUI/Properties/Resources.resx b/GUI/Properties/Resources.resx index b0a6f2c36..0867ac38f 100644 --- a/GUI/Properties/Resources.resx +++ b/GUI/Properties/Resources.resx @@ -344,6 +344,13 @@ You can refresh the mod list manually with the Refresh button at the top, and yo Directory "{0}" doesn't exist. {0} (INVALID) {0} (LOCKED) + Not a valid game instance file: "{0}" + +If you selected a shortcut, don't do that; Windows won't let us hide them from this dialog even though they're invalid. + +Find the folder where your game is installed and choose one of these files: + +{1} Failed to fetch master list. CKAN Plugins (*.dll)|*.dll {0} files, {1}, {2} free diff --git a/Netkan/Transformers/InternalCkanTransformer.cs b/Netkan/Transformers/InternalCkanTransformer.cs index 044b3db2f..7d75ab000 100644 --- a/Netkan/Transformers/InternalCkanTransformer.cs +++ b/Netkan/Transformers/InternalCkanTransformer.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using log4net; -using CKAN.Versioning; using CKAN.NetKAN.Extensions; using CKAN.NetKAN.Model; using CKAN.NetKAN.Services; diff --git a/Netkan/Transformers/MetaNetkanTransformer.cs b/Netkan/Transformers/MetaNetkanTransformer.cs index f20ca66fe..ace4040ae 100644 --- a/Netkan/Transformers/MetaNetkanTransformer.cs +++ b/Netkan/Transformers/MetaNetkanTransformer.cs @@ -5,7 +5,6 @@ using log4net; using Newtonsoft.Json.Linq; -using CKAN.Versioning; using CKAN.NetKAN.Extensions; using CKAN.NetKAN.Model; using CKAN.NetKAN.Services;