From 667bb411beaf959ba19d8c21218fbf1473dad3e9 Mon Sep 17 00:00:00 2001 From: DasSkelett Date: Fri, 5 Mar 2021 15:06:31 +0100 Subject: [PATCH 1/3] Fixes for .ckan-installed mods and other GUI fixes * Append installed mod version in `AllModVersions` tab if not known to registry (e.g. if installed from local .ckan) * Don't mark .ckan-installed mods as incompatible * Add OK button to wait tab page; it is enabled if the install process failed or the user cancelled too late. When clicked it hides the tab and calls `UpdateModsList()` to clear/update the changeset. * Remove queued changes of incompatible modules and from the changeset when clearing changeset * Remove queued removals for mods whose AutoInstall checkbox has been checked without dependent mod installed when clearing changeset * Prevent `installWorker` from being run twice if a user double-clicks the "Accept" button * Make `menuStrip2` overflowable * Fix opening instance directory in `ManageGameInstancesDialog` * Don't offer upgrades that would violate other modules' dependencies * Prevent `NullReferenceException` in `NetAsyncDownloader.FileProgressReport()` --- Core/Net/NetAsyncDownloader.cs | 5 +++ GUI/Controls/AllModVersions.cs | 21 +++++++++- GUI/Controls/ManageMods.Designer.cs | 12 +++++- GUI/Controls/ManageMods.cs | 13 ++++++ GUI/Controls/ModInfo.cs | 2 +- GUI/Controls/Wait.Designer.cs | 53 +++++++++++++++--------- GUI/Controls/Wait.cs | 11 +++++ GUI/Controls/Wait.resx | 1 + GUI/Dialogs/ManageGameInstancesDialog.cs | 4 +- GUI/Main/Main.Designer.cs | 5 ++- GUI/Main/MainChangeset.cs | 24 +++++++---- GUI/Main/MainInstall.cs | 18 +++++--- GUI/Main/MainWait.cs | 12 ++++++ GUI/Model/GUIMod.cs | 14 ++++++- 14 files changed, 155 insertions(+), 40 deletions(-) diff --git a/Core/Net/NetAsyncDownloader.cs b/Core/Net/NetAsyncDownloader.cs index c31794bd45..f2367edd40 100644 --- a/Core/Net/NetAsyncDownloader.cs +++ b/Core/Net/NetAsyncDownloader.cs @@ -356,6 +356,8 @@ private void FileProgressReport(int index, int percent, long bytesDownloaded, lo foreach (NetAsyncDownloaderDownloadPart t in downloads.ToList()) { + if (t == null) + continue; if (t.bytesLeft > 0) { totalBytesPerSecond += t.bytesPerSecond; @@ -366,6 +368,9 @@ private void FileProgressReport(int index, int percent, long bytesDownloaded, lo } foreach (Net.DownloadTarget t in queuedDownloads.ToList()) { + // Somehow managed to get a NullRef for t here + if (t == null) + continue; totalBytesLeft += t.size; totalSize += t.size; } diff --git a/GUI/Controls/AllModVersions.cs b/GUI/Controls/AllModVersions.cs index 2641c598a9..5586ca737a 100644 --- a/GUI/Controls/AllModVersions.cs +++ b/GUI/Controls/AllModVersions.cs @@ -122,6 +122,11 @@ public GUIMod SelectedModule visibleGuiModule.PropertyChanged += visibleGuiModule_PropertyChanged; } } + VersionsListView.Items.Clear(); + if (value == null) + { + return; + } // Get all the data; can put this in bg if slow GameInstance currentInstance = Main.Instance.Manager.CurrentInstance; @@ -139,16 +144,28 @@ public GUIMod SelectedModule } catch (ModuleNotFoundKraken) { - // No versions to be shown, abort and hope an auto refresh happens + // Identifier unknown to registry, maybe installed from local .ckan + allAvailableVersions = new Dictionary(); + } + + // Take the module associated with GUIMod, if any, and append it to the list if it's not already there. + var installedModule = value.InstalledMod?.Module; + if (installedModule != null && !allAvailableVersions.ContainsKey(installedModule)) + { + allAvailableVersions.Add(installedModule, installable(installer, installedModule, registry)); + } + + if (!allAvailableVersions.Any()) + { return; } + ModuleVersion installedVersion = registry.InstalledVersion(value.Identifier); // Update UI; must be in fg ignoreItemCheck = true; bool latestCompatibleVersionAlreadyFound = false; - VersionsListView.Items.Clear(); // Only show checkboxes for non-DLC modules VersionsListView.CheckBoxes = !value.ToModule().IsDLC; diff --git a/GUI/Controls/ManageMods.Designer.cs b/GUI/Controls/ManageMods.Designer.cs index 1e4ac0a7b5..294b8e574e 100644 --- a/GUI/Controls/ManageMods.Designer.cs +++ b/GUI/Controls/ManageMods.Designer.cs @@ -1,4 +1,6 @@ -namespace CKAN +using System.Windows.Forms; + +namespace CKAN { partial class ManageMods { @@ -98,6 +100,7 @@ private void InitializeComponent() this.FilterToolButton, this.NavBackwardToolButton, this.NavForwardToolButton}); + this.menuStrip2.CanOverflow = true; this.menuStrip2.Location = new System.Drawing.Point(0, 0); this.menuStrip2.Name = "menuStrip2"; this.menuStrip2.Size = new System.Drawing.Size(5876, 48); @@ -110,6 +113,7 @@ private void InitializeComponent() this.launchGameToolStripMenuItem.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; this.launchGameToolStripMenuItem.Name = "launchGameToolStripMenuItem"; this.launchGameToolStripMenuItem.Size = new System.Drawing.Size(146, 56); + this.launchGameToolStripMenuItem.Overflow = ToolStripItemOverflow.AsNeeded; this.launchGameToolStripMenuItem.Click += new System.EventHandler(this.launchGameToolStripMenuItem_Click); resources.ApplyResources(this.launchGameToolStripMenuItem, "launchGameToolStripMenuItem"); // @@ -119,6 +123,7 @@ private void InitializeComponent() this.RefreshToolButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; this.RefreshToolButton.Name = "RefreshToolButton"; this.RefreshToolButton.Size = new System.Drawing.Size(114, 56); + this.RefreshToolButton.Overflow = ToolStripItemOverflow.AsNeeded; this.RefreshToolButton.Click += new System.EventHandler(this.RefreshToolButton_Click); resources.ApplyResources(this.RefreshToolButton, "RefreshToolButton"); // @@ -128,6 +133,7 @@ private void InitializeComponent() this.UpdateAllToolButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; this.UpdateAllToolButton.Name = "UpdateAllToolButton"; this.UpdateAllToolButton.Size = new System.Drawing.Size(232, 56); + this.UpdateAllToolButton.Overflow = ToolStripItemOverflow.AsNeeded; this.UpdateAllToolButton.Click += new System.EventHandler(this.MarkAllUpdatesToolButton_Click); resources.ApplyResources(this.UpdateAllToolButton, "UpdateAllToolButton"); // @@ -137,6 +143,7 @@ private void InitializeComponent() this.ApplyToolButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; this.ApplyToolButton.Name = "ApplyToolButton"; this.ApplyToolButton.Size = new System.Drawing.Size(173, 56); + this.ApplyToolButton.Overflow = ToolStripItemOverflow.AsNeeded; this.ApplyToolButton.Click += new System.EventHandler(this.ApplyToolButton_Click); resources.ApplyResources(this.ApplyToolButton, "ApplyToolButton"); // @@ -161,6 +168,7 @@ private void InitializeComponent() this.FilterToolButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; this.FilterToolButton.Name = "FilterToolButton"; this.FilterToolButton.Size = new System.Drawing.Size(201, 56); + this.FilterToolButton.Overflow = ToolStripItemOverflow.AsNeeded; resources.ApplyResources(this.FilterToolButton, "FilterToolButton"); // // FilterCompatibleButton @@ -253,6 +261,7 @@ private void InitializeComponent() this.NavBackwardToolButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; this.NavBackwardToolButton.Name = "NavBackwardToolButton"; this.NavBackwardToolButton.Size = new System.Drawing.Size(44, 56); + this.NavBackwardToolButton.Overflow = ToolStripItemOverflow.AsNeeded; this.NavBackwardToolButton.Click += new System.EventHandler(this.NavBackwardToolButton_Click); resources.ApplyResources(this.NavBackwardToolButton, "NavBackwardToolButton"); // @@ -262,6 +271,7 @@ private void InitializeComponent() this.NavForwardToolButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; this.NavForwardToolButton.Name = "NavForwardToolButton"; this.NavForwardToolButton.Size = new System.Drawing.Size(44, 56); + this.NavForwardToolButton.Overflow = ToolStripItemOverflow.AsNeeded; this.NavForwardToolButton.Click += new System.EventHandler(this.NavForwardToolButton_Click); resources.ApplyResources(this.NavForwardToolButton, "NavForwardToolButton"); // diff --git a/GUI/Controls/ManageMods.cs b/GUI/Controls/ManageMods.cs index 76eb2492a1..36106baa26 100644 --- a/GUI/Controls/ManageMods.cs +++ b/GUI/Controls/ManageMods.cs @@ -786,8 +786,21 @@ public void ClearChangeSet() { mod.SetInstallChecked(row, Installed, mod.IsInstalled); } + else if (mod.SelectedMod != mod.InstalledMod?.Module) + { + mod.SelectedMod = mod.InstalledMod?.Module; + } mod.SetUpgradeChecked(row, UpdateCol, false); mod.SetReplaceChecked(row, ReplaceCol, false); + // Marking a mod as AutoInstalled can immediately queue it for removal if there is no dependent mod. + // Reset the state of the AutoInstalled checkbox for these by deducing it from the changeset. + if (mod.InstalledMod != null && + ChangeSet.Contains(new ModChange(mod.InstalledMod?.Module, GUIModChangeType.Remove, + new SelectionReason.NoLongerUsed())) + ) + { + mod.SetAutoInstallChecked(row, AutoInstalled, false); + } } } diff --git a/GUI/Controls/ModInfo.cs b/GUI/Controls/ModInfo.cs index 714eeeb3fb..5084358d43 100644 --- a/GUI/Controls/ModInfo.cs +++ b/GUI/Controls/ModInfo.cs @@ -140,7 +140,7 @@ private void UpdateModInfo(GUIMod gui_module) Util.Invoke(MetadataModuleAuthorTextBox, () => MetadataModuleAuthorTextBox.Text = gui_module.Authors); Util.Invoke(MetadataIdentifierTextBox, () => MetadataIdentifierTextBox.Text = module.identifier); - Util.Invoke(MetadataModuleReleaseStatusTextBox, () => + Util.Invoke(MetadataModuleReleaseStatusTextBox, () => { if (module.release_status == null) { diff --git a/GUI/Controls/Wait.Designer.cs b/GUI/Controls/Wait.Designer.cs index 51b994eca0..8f6715cb71 100644 --- a/GUI/Controls/Wait.Designer.cs +++ b/GUI/Controls/Wait.Designer.cs @@ -37,18 +37,19 @@ private void InitializeComponent() this.BottomButtonPanel = new System.Windows.Forms.Panel(); this.CancelCurrentActionButton = new System.Windows.Forms.Button(); this.RetryCurrentActionButton = new System.Windows.Forms.Button(); + this.OkButton = new System.Windows.Forms.Button(); this.SuspendLayout(); - // + // // TopPanel - // + // this.TopPanel.Controls.Add(this.MessageTextBox); this.TopPanel.Controls.Add(this.DialogProgressBar); this.TopPanel.Dock = System.Windows.Forms.DockStyle.Top; this.TopPanel.Name = "TopPanel"; this.TopPanel.Size = new System.Drawing.Size(500, 85); - // + // // MessageTextBox - // + // this.MessageTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.MessageTextBox.BackColor = System.Drawing.SystemColors.Control; this.MessageTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None; @@ -62,9 +63,9 @@ private void InitializeComponent() this.MessageTextBox.TabIndex = 0; this.MessageTextBox.TextAlign = System.Windows.Forms.HorizontalAlignment.Center; resources.ApplyResources(this.MessageTextBox, "MessageTextBox"); - // + // // DialogProgressBar - // + // this.DialogProgressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.DialogProgressBar.Location = new System.Drawing.Point(5, 45); @@ -75,9 +76,9 @@ private void InitializeComponent() this.DialogProgressBar.Size = new System.Drawing.Size(490, 25); this.DialogProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee; this.DialogProgressBar.TabIndex = 1; - // + // // LogTextBox - // + // this.LogTextBox.Dock = System.Windows.Forms.DockStyle.Fill; this.LogTextBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.LogTextBox.Location = new System.Drawing.Point(14, 89); @@ -89,41 +90,54 @@ private void InitializeComponent() this.LogTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; this.LogTextBox.Size = new System.Drawing.Size(500, 400); this.LogTextBox.TabIndex = 2; - // + // // BottomButtonPanel - // - this.BottomButtonPanel.Controls.Add(this.CancelCurrentActionButton); + // this.BottomButtonPanel.Controls.Add(this.RetryCurrentActionButton); + this.BottomButtonPanel.Controls.Add(this.CancelCurrentActionButton); + this.BottomButtonPanel.Controls.Add(this.OkButton); this.BottomButtonPanel.Dock = System.Windows.Forms.DockStyle.Bottom; this.BottomButtonPanel.Name = "BottomButtonPanel"; this.BottomButtonPanel.Size = new System.Drawing.Size(500, 40); - // + // // RetryCurrentActionButton - // + // this.RetryCurrentActionButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.RetryCurrentActionButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.RetryCurrentActionButton.Location = new System.Drawing.Point(266, 5); + this.RetryCurrentActionButton.Location = new System.Drawing.Point(149, 5); this.RetryCurrentActionButton.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.RetryCurrentActionButton.Name = "RetryCurrentActionButton"; this.RetryCurrentActionButton.Size = new System.Drawing.Size(112, 30); this.RetryCurrentActionButton.TabIndex = 3; this.RetryCurrentActionButton.Click += new System.EventHandler(this.RetryCurrentActionButton_Click); resources.ApplyResources(this.RetryCurrentActionButton, "RetryCurrentActionButton"); - // + // // CancelCurrentActionButton - // + // this.CancelCurrentActionButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.CancelCurrentActionButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this.CancelCurrentActionButton.Location = new System.Drawing.Point(383, 5); + this.CancelCurrentActionButton.Location = new System.Drawing.Point(266, 5); this.CancelCurrentActionButton.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); this.CancelCurrentActionButton.Name = "CancelCurrentActionButton"; this.CancelCurrentActionButton.Size = new System.Drawing.Size(112, 30); this.CancelCurrentActionButton.TabIndex = 4; this.CancelCurrentActionButton.Click += new System.EventHandler(this.CancelCurrentActionButton_Click); resources.ApplyResources(this.CancelCurrentActionButton, "CancelCurrentActionButton"); - // + // + // OkButton + // + this.OkButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.OkButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; + this.OkButton.Location = new System.Drawing.Point(383, 5); + this.OkButton.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.OkButton.Name = "OkButton"; + this.OkButton.Size = new System.Drawing.Size(112, 30); + this.OkButton.TabIndex = 5; + this.OkButton.Click += new System.EventHandler(this.OkButton_Click); + resources.ApplyResources(this.OkButton, "OkButton"); + // // Wait - // + // this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; this.Controls.Add(this.LogTextBox); this.Controls.Add(this.TopPanel); @@ -146,5 +160,6 @@ private void InitializeComponent() private System.Windows.Forms.Panel BottomButtonPanel; private System.Windows.Forms.Button RetryCurrentActionButton; private System.Windows.Forms.Button CancelCurrentActionButton; + private System.Windows.Forms.Button OkButton; } } diff --git a/GUI/Controls/Wait.cs b/GUI/Controls/Wait.cs index b2016bfb2a..36bda98f82 100644 --- a/GUI/Controls/Wait.cs +++ b/GUI/Controls/Wait.cs @@ -17,6 +17,7 @@ public Wait() public event Action OnRetry; public event Action OnCancel; + public event Action OnOk; public bool RetryEnabled { @@ -199,6 +200,7 @@ public void Reset(bool cancelable) ProgressIndeterminate = true; RetryCurrentActionButton.Enabled = false; CancelCurrentActionButton.Enabled = cancelable; + OkButton.Enabled = false; MessageTextBox.Text = Properties.Resources.MainWaitPleaseWait; }); } @@ -212,6 +214,7 @@ public void Finish(bool success) ProgressIndeterminate = false; RetryCurrentActionButton.Enabled = !success; CancelCurrentActionButton.Enabled = false; + OkButton.Enabled = true; }); } @@ -250,5 +253,13 @@ private void CancelCurrentActionButton_Click(object sender, EventArgs e) Util.Invoke(this, () => CancelCurrentActionButton.Enabled = false); } + + private void OkButton_Click(object sender, EventArgs e) + { + if (OnOk != null) + { + OnOk(); + } + } } } diff --git a/GUI/Controls/Wait.resx b/GUI/Controls/Wait.resx index 4ac4273fb8..e273d4364b 100644 --- a/GUI/Controls/Wait.resx +++ b/GUI/Controls/Wait.resx @@ -119,5 +119,6 @@ Cancel Retry + OK Waiting for operation to complete diff --git a/GUI/Dialogs/ManageGameInstancesDialog.cs b/GUI/Dialogs/ManageGameInstancesDialog.cs index 2158b625d7..4a04afafb0 100644 --- a/GUI/Dialogs/ManageGameInstancesDialog.cs +++ b/GUI/Dialogs/ManageGameInstancesDialog.cs @@ -19,7 +19,7 @@ public partial class ManageGameInstancesDialog : Form Filter = GameFolderFilter(Main.Instance.Manager), Multiselect = false }; - + /// /// Generate filter string for OpenFileDialog /// @@ -213,7 +213,7 @@ private void GameInstancesListView_Click(object sender, MouseEventArgs e) private void OpenDirectoryMenuItem_Click(object sender, EventArgs e) { - string path = GameInstancesListView.SelectedItems[0].SubItems[2].Text; + string path = _manager.Instances[(string) GameInstancesListView.SelectedItems[0].Tag].GameDir(); if (!Directory.Exists(path)) { diff --git a/GUI/Main/Main.Designer.cs b/GUI/Main/Main.Designer.cs index 2fb74ff161..17e51b02fe 100644 --- a/GUI/Main/Main.Designer.cs +++ b/GUI/Main/Main.Designer.cs @@ -401,9 +401,9 @@ private void InitializeComponent() this.ManageModsTabPage.Size = new System.Drawing.Size(1536, 948); this.ManageModsTabPage.TabIndex = 3; resources.ApplyResources(this.ManageModsTabPage, "ManageModsTabPage"); - // + // // ManageMods - // + // this.ManageMods.Dock = System.Windows.Forms.DockStyle.Fill; this.ManageMods.Location = new System.Drawing.Point(0, 0); this.ManageMods.Margin = new System.Windows.Forms.Padding(0,0,0,0); @@ -467,6 +467,7 @@ private void InitializeComponent() this.Wait.TabIndex = 32; this.Wait.OnRetry += Wait_OnRetry; this.Wait.OnCancel += Wait_OnCancel; + this.Wait.OnOk += Wait_OnOk; // // ChooseRecommendedModsTabPage // diff --git a/GUI/Main/MainChangeset.cs b/GUI/Main/MainChangeset.cs index db2935f90c..51d59d51cd 100644 --- a/GUI/Main/MainChangeset.cs +++ b/GUI/Main/MainChangeset.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Collections.Generic; using System.Windows.Forms; @@ -37,13 +38,22 @@ private void Changeset_OnConfirmChanges() // Using the changeset passed in can cause issues with versions. // An example is Mechjeb for FAR at 25/06/2015 with a 1.0.2 install. // TODO Work out why this is. - installWorker.RunWorkerAsync( - new KeyValuePair, RelationshipResolverOptions>( - ManageMods.mainModList.ComputeUserChangeSet(RegistryManager.Instance(Main.Instance.CurrentInstance).registry).ToList(), - RelationshipResolver.DependsOnlyOpts() - ) - ); + try + { + installWorker.RunWorkerAsync( + new KeyValuePair, RelationshipResolverOptions>( + ManageMods.mainModList + .ComputeUserChangeSet(RegistryManager.Instance(Main.Instance.CurrentInstance).registry) + .ToList(), + RelationshipResolver.DependsOnlyOpts() + ) + ); + } + catch (InvalidOperationException) + { + // Thrown if it's already busy, can happen if the user fouble-clicks the button. Ignore it. + // More thread-safe than checking installWorker.IsBusy beforehand. + } } - } } diff --git a/GUI/Main/MainInstall.cs b/GUI/Main/MainInstall.cs index e879dd83d4..09ed64db22 100644 --- a/GUI/Main/MainInstall.cs +++ b/GUI/Main/MainInstall.cs @@ -142,6 +142,8 @@ out Dictionary> supporters if (installCanceled) { + Util.Invoke(this, () => Enabled = true); + Util.Invoke(menuStrip1, () => menuStrip1.Enabled = true); tabController.ShowTab("ManageModsTabPage"); e.Result = new KeyValuePair(false, opts.Key); return; @@ -236,6 +238,8 @@ out Dictionary> supporters { // User cancelled, get out tabController.ShowTab("ManageModsTabPage"); + Util.Invoke(this, () => Enabled = true); + Util.Invoke(menuStrip1, () => menuStrip1.Enabled = true); e.Result = new KeyValuePair(false, opts.Key); return; } @@ -291,7 +295,11 @@ private void OnModInstalled(CkanModule mod) private void PostInstallMods(object sender, RunWorkerCompletedEventArgs e) { - tabController.SetTabLock(false); + okCallback = () => + { + ManageMods.UpdateModsList(null); + okCallback = null; + }; if (e.Error != null) { @@ -410,7 +418,10 @@ private void PostInstallMods(object sender, RunWorkerCompletedEventArgs e) } } - // install successful + Util.Invoke(this, () => Enabled = true); + Util.Invoke(menuStrip1, () => menuStrip1.Enabled = true); + tabController.SetTabLock(false); + AddStatusMessage(Properties.Resources.MainInstallSuccess); HideWaitDialog(true); } @@ -434,9 +445,6 @@ private void PostInstallMods(object sender, RunWorkerCompletedEventArgs e) } } } - - Util.Invoke(this, () => Enabled = true); - Util.Invoke(menuStrip1, () => menuStrip1.Enabled = true); } } } diff --git a/GUI/Main/MainWait.cs b/GUI/Main/MainWait.cs index eb23a69c19..bddfaf3334 100644 --- a/GUI/Main/MainWait.cs +++ b/GUI/Main/MainWait.cs @@ -6,6 +6,7 @@ namespace CKAN public partial class Main { private Action cancelCallback; + private Action okCallback; public void ShowWaitDialog(bool cancelable = true) { @@ -87,5 +88,16 @@ public void Wait_OnCancel() cancelCallback(); } } + + public void Wait_OnOk() + { + if (okCallback != null) + { + okCallback(); + } + Util.Invoke(this, () => Enabled = true); + Util.Invoke(menuStrip1, () => menuStrip1.Enabled = true); + tabController.SetTabLock(false); + } } } diff --git a/GUI/Model/GUIMod.cs b/GUI/Model/GUIMod.cs index b45152d09d..6737390908 100644 --- a/GUI/Model/GUIMod.cs +++ b/GUI/Model/GUIMod.cs @@ -12,7 +12,7 @@ public sealed class GUIMod : INotifyPropertyChanged { private CkanModule Mod { get; set; } private CkanModule LatestCompatibleMod { get; set; } - private InstalledModule InstalledMod { get; set; } + public InstalledModule InstalledMod { get; private set; } /// /// The module of the checkbox that is checked in the MainAllModVersions list if any, @@ -141,6 +141,8 @@ public GUIMod(InstalledModule instMod, IRegistryQuerier registry, GameVersionCri { LatestVersion = InstalledVersion; } + // For mods not known to the registry LatestCompatibleMod is null, however the installed module might be compatible + IsIncompatible = incompatible ?? LatestCompatibleMod == null && !instMod.Module.IsCompatibleKSP(current_game_version); } /// @@ -268,6 +270,12 @@ public void UpdateIsCached() IsCached = Main.Instance.Manager.Cache.IsMaybeCachedZip(Mod); } + /// + /// Get the CkanModule associated with this GUIMod. + /// See for a method that doesn't throw. + /// + /// The CkanModule associated with this GUIMod + /// Thrown if no CkanModule is associated public CkanModule ToCkanModule() { if (!IsCKAN) @@ -276,6 +284,10 @@ public CkanModule ToCkanModule() return mod; } + /// + /// Get the CkanModule associated with this GUIMod. + /// + /// The CkanModule associated with this GUIMod or null if there is none public CkanModule ToModule() { return Mod; From 38b9cf3a2890c47ef498eaee344cdf0be3f86a0c Mon Sep 17 00:00:00 2001 From: DasSkelett Date: Tue, 2 Mar 2021 16:04:03 +0100 Subject: [PATCH 2/3] Don't offer mod upgrade if it would violate another mod's dependencies In 'IRegistryQuerierHelpers.HasUpdate()' and 'ModuleInstaller.CanInstall()' we didn't pass the currently installed module version as 'moduleToRemove', thus the RelationshipResolver couldn't detect if another mod depending on a specific version of a mod would break. This made the GUI offer upgrades that would fail during the actual installation, and the 'Versions' tab listing versions as compatible that weren't. --- Core/ModuleInstaller.cs | 2 +- Core/Registry/IRegistryQuerier.cs | 2 +- Core/Relationships/RelationshipResolver.cs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Core/ModuleInstaller.cs b/Core/ModuleInstaller.cs index 3c016494a9..e3d4009ff2 100644 --- a/Core/ModuleInstaller.cs +++ b/Core/ModuleInstaller.cs @@ -1347,7 +1347,7 @@ IRegistryQuerier registry { RelationshipResolver resolver = new RelationshipResolver( toInstall, - null, + toInstall.Select(m => registry.InstalledModule(m.identifier)?.Module).Where(m => m != null), opts, registry, ksp.VersionCriteria() ); diff --git a/Core/Registry/IRegistryQuerier.cs b/Core/Registry/IRegistryQuerier.cs index 1a4e6e87bb..46b272e0f1 100644 --- a/Core/Registry/IRegistryQuerier.cs +++ b/Core/Registry/IRegistryQuerier.cs @@ -194,7 +194,7 @@ public static bool HasUpdate(this IRegistryQuerier querier, string identifier, G { RelationshipResolver resolver = new RelationshipResolver( new CkanModule[] { newest_version }, - null, + new CkanModule[] { querier.InstalledModule(identifier).Module }, new RelationshipResolverOptions() { with_recommends = false, diff --git a/Core/Relationships/RelationshipResolver.cs b/Core/Relationships/RelationshipResolver.cs index 87fd39ddca..9b45e04ee9 100644 --- a/Core/Relationships/RelationshipResolver.cs +++ b/Core/Relationships/RelationshipResolver.cs @@ -247,9 +247,9 @@ private void AddModulesToInstall(IEnumerable modules) { log.DebugFormat("Preparing to resolve relationships for {0} {1}", module.identifier, module.version); - //Need to check against installed mods and those to install. - var mods = modlist.Values.Concat(installed_modules).Where(listed_mod => listed_mod.ConflictsWith(module)); - foreach (CkanModule listed_mod in mods) + // Need to check against installed mods and those to install. + var conflictingModules = modlist.Values.Concat(installed_modules).Where(listed_mod => listed_mod.ConflictsWith(module)); + foreach (CkanModule listed_mod in conflictingModules) { if (options.proceed_with_inconsistencies) { From 91c207131142bfa131a5483c7f904202efa2a4d2 Mon Sep 17 00:00:00 2001 From: DasSkelett Date: Thu, 4 Mar 2021 20:11:03 +0100 Subject: [PATCH 3/3] Refresh registry when loading an instance with empty registry in ConsoleUI --- ConsoleUI/ConsoleCKAN.cs | 4 ++-- ConsoleUI/ModListScreen.cs | 42 ++++++++++++++++++++++---------------- 2 files changed, 26 insertions(+), 20 deletions(-) diff --git a/ConsoleUI/ConsoleCKAN.cs b/ConsoleUI/ConsoleCKAN.cs index 0e34799f13..c8d2dbd38c 100644 --- a/ConsoleUI/ConsoleCKAN.cs +++ b/ConsoleUI/ConsoleCKAN.cs @@ -16,7 +16,7 @@ public class ConsoleCKAN { /// public ConsoleCKAN(GameInstanceManager mgr, string themeName, bool debug) { - if (ConsoleTheme.Themes.TryGetValue(themeName, out ConsoleTheme theme)) + if (ConsoleTheme.Themes.TryGetValue(themeName ?? "default", out ConsoleTheme theme)) { // GameInstanceManager only uses its IUser object to construct game instance objects, // which only use it to inform the user about the creation of the CKAN/ folder. @@ -40,7 +40,7 @@ public ConsoleCKAN(GameInstanceManager mgr, string themeName, bool debug) } } if (manager.CurrentInstance != null) { - new ModListScreen(manager, debug).Run(theme); + new ModListScreen(manager, debug, theme).Run(theme); } new ExitScreen().Run(theme); diff --git a/ConsoleUI/ModListScreen.cs b/ConsoleUI/ModListScreen.cs index 96c11a0de8..cbc5555375 100644 --- a/ConsoleUI/ModListScreen.cs +++ b/ConsoleUI/ModListScreen.cs @@ -17,7 +17,7 @@ public class ModListScreen : ConsoleScreen { /// /// Game instance manager object containing the current instance /// True if debug options should be available, false otherwise - public ModListScreen(GameInstanceManager mgr, bool dbg) + public ModListScreen(GameInstanceManager mgr, bool dbg, ConsoleTheme _theme) { debug = dbg; manager = mgr; @@ -25,7 +25,7 @@ public ModListScreen(GameInstanceManager mgr, bool dbg) moduleList = new ConsoleListBox( 1, 4, -1, -2, - GetAllMods(), + GetAllMods(_theme), new List>() { new ConsoleListBoxColumn() { Header = "", @@ -205,7 +205,7 @@ public ModListScreen(GameInstanceManager mgr, bool dbg) } return true; }); - + moduleList.AddTip("F8", "Mark auto-installed", () => moduleList.Selection != null && !moduleList.Selection.IsDLC && (!registry.InstalledModule(moduleList.Selection.identifier)?.AutoInstalled ?? false) @@ -265,7 +265,7 @@ public ModListScreen(GameInstanceManager mgr, bool dbg) null, new ConsoleMenuOption("Refresh mod list", "F5, Ctrl+R", "Refresh the list of mods", - true, UpdateRegistry), + true, (ConsoleTheme th) => UpdateRegistry(th)), new ConsoleMenuOption("Upgrade all", "Ctrl+U", "Mark all available updates for installation", true, UpgradeAll, null, null, HasAnyUpgradeable()), @@ -328,7 +328,7 @@ protected override string CenterHeader() private bool ImportDownloads(ConsoleTheme theme) { DownloadImportDialog.ImportDownloads(theme, manager.CurrentInstance, manager.Cache, plan); - RefreshList(); + RefreshList(theme); return true; } @@ -393,7 +393,7 @@ private bool ViewSuggestions(ConsoleTheme theme) } } if (needRefresh) { - RefreshList(); + RefreshList(theme); } } else { RaiseError("Installed mods have no unsatisfied recommendations or suggestions."); @@ -414,7 +414,7 @@ private int daysSinceUpdated(string filename) return (DateTime.Now - File.GetLastWriteTime(filename)).Days; } - private bool UpdateRegistry(ConsoleTheme theme) + private bool UpdateRegistry(ConsoleTheme theme, bool showNewModsPrompt = true) { ProgressScreen ps = new ProgressScreen("Updating Registry", "Checking for updates"); LaunchSubScreen(theme, ps, (ConsoleTheme th) => { @@ -447,11 +447,11 @@ private bool UpdateRegistry(ConsoleTheme theme) } } }); - if (recent.Count > 0 && RaiseYesNoDialog(newModPrompt(recent.Count))) { + if (showNewModsPrompt && recent.Count > 0 && RaiseYesNoDialog(newModPrompt(recent.Count))) { searchBox.Clear(); moduleList.FilterString = searchBox.Value = "~n"; } - RefreshList(); + RefreshList(theme); return true; } @@ -481,7 +481,7 @@ private bool SelectInstall(ConsoleTheme theme) if (!prevInst.Equals(manager.CurrentInstance)) { plan.Reset(); registry = RegistryManager.Instance(manager.CurrentInstance).registry; - RefreshList(); + RefreshList(theme); } return true; } @@ -492,17 +492,23 @@ private bool EditAuthTokens(ConsoleTheme theme) return true; } - private void RefreshList() + private void RefreshList(ConsoleTheme theme) { - moduleList.SetData(GetAllMods(true)); + // 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)); } private List allMods = null; - private List GetAllMods(bool force = false) + private List GetAllMods(ConsoleTheme theme, bool force = false) { ScanForMods(); if (allMods == null || force) { + if (!registry?.HasAnyAvailable() ?? false) + { + UpdateRegistry(theme, false); + } allMods = new List(registry.CompatibleModules(manager.CurrentInstance.VersionCriteria())); foreach (InstalledModule im in registry.InstalledModules) { CkanModule m = null; @@ -546,7 +552,7 @@ private bool Help(ConsoleTheme theme) private bool ApplyChanges(ConsoleTheme theme) { LaunchSubScreen(theme, new InstallScreen(manager, plan, debug)); - RefreshList(); + RefreshList(theme); return true; } @@ -598,9 +604,9 @@ private long totalInstalledDownloadSize() return total; } - private GameInstanceManager manager; - private IRegistryQuerier registry; - private bool debug; + private GameInstanceManager manager; + private Registry registry; + private bool debug; private ConsoleField searchBox; private ConsoleListBox moduleList; @@ -839,7 +845,7 @@ public enum InstallStatus { /// This mod is installed and not upgradeable or planned to be removed /// Installed, - + /// /// Like Installed, but can be auto-removed if depending mods are removed ///