Skip to content

Commit

Permalink
Show conflicts in changeset
Browse files Browse the repository at this point in the history
  • Loading branch information
HebaruSan committed Feb 27, 2023
1 parent 04034c0 commit 2fc0dbb
Show file tree
Hide file tree
Showing 7 changed files with 71 additions and 143 deletions.
25 changes: 16 additions & 9 deletions GUI/Controls/Changeset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ public Changeset()
InitializeComponent();
}

public void LoadChangeset(List<ModChange> changes, List<ModuleLabel> AlertLabels)
public void LoadChangeset(
List<ModChange> changes,
List<ModuleLabel> AlertLabels,
Dictionary<CkanModule, string> conflicts)
{
changeset = changes;
alertLabels = AlertLabels;
Expand All @@ -25,11 +28,12 @@ public void LoadChangeset(List<ModChange> changes, List<ModuleLabel> AlertLabels
// Changeset sorting is handled upstream in the resolver
ChangesListView.Items.AddRange(changes
.Where(ch => ch.ChangeType != GUIModChangeType.None)
.Select(makeItem)
.Select(ch => makeItem(ch, conflicts))
.ToArray());
ChangesListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
ChangesListView.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize);
}
ConfirmChangesButton.Enabled = conflicts == null || !conflicts.Any();
}

protected override void OnVisibleChanged(EventArgs e)
Expand Down Expand Up @@ -71,7 +75,7 @@ private void BackButton_Click(object sender, EventArgs e)
OnCancelChanges?.Invoke(false);
}

private ListViewItem makeItem(ModChange change)
private ListViewItem makeItem(ModChange change, Dictionary<CkanModule, string> conflicts)
{
var descr = change.Description;
CkanModule m = change.Mod;
Expand All @@ -80,16 +84,19 @@ private ListViewItem makeItem(ModChange change)
{
change.NameAndStatus,
change.ChangeType.ToI18nString(),
warnLbl != null
? string.Format(
Properties.Resources.MainChangesetWarningInstallingModuleWithLabel,
warnLbl.Name,
descr)
: descr
conflicts != null && conflicts.TryGetValue(m, out string confDescr)
? string.Format("{0} ({1})", confDescr, descr)
: warnLbl != null
? string.Format(
Properties.Resources.MainChangesetWarningInstallingModuleWithLabel,
warnLbl.Name,
descr)
: descr
})
{
Tag = m,
ForeColor = warnLbl != null ? Color.Red : SystemColors.WindowText,
BackColor = conflicts != null && conflicts.ContainsKey(m) ? Color.LightCoral : Color.Empty,
ToolTipText = descr,
};
}
Expand Down
2 changes: 2 additions & 0 deletions GUI/Controls/ManageMods.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

80 changes: 22 additions & 58 deletions GUI/Controls/ManageMods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,24 +84,18 @@ private List<string> sortColumns
}
}

private List<bool> descending
{
get
{
return Main.Instance.configuration.MultiSortDescending;
}
}
private List<bool> descending => Main.Instance.configuration.MultiSortDescending;

public event Action<GUIMod> OnSelectedModuleChanged;
public event Action<List<ModChange>> OnChangeSetChanged;
public event Action<List<ModChange>, Dictionary<GUIMod, string>> OnChangeSetChanged;
public event Action OnRegistryChanged;

public event Action<List<ModChange>> StartChangeSet;
public event Action<IEnumerable<GUIMod>> LabelsAfterUpdate;

private List<ModChange> ChangeSet
{
get { return currentChangeSet; }
get => currentChangeSet;
set
{
var orig = currentChangeSet;
Expand All @@ -124,13 +118,13 @@ private void ChangeSetUpdated()
ApplyToolButton.Enabled = false;
InstallAllCheckbox.Checked = true;
}
OnChangeSetChanged?.Invoke(ChangeSet);
OnChangeSetChanged?.Invoke(ChangeSet, Conflicts);
});
}

private Dictionary<GUIMod, string> Conflicts
{
get { return conflicts; }
get => conflicts;
set
{
var orig = conflicts;
Expand Down Expand Up @@ -1589,30 +1583,17 @@ protected override bool ProcessCmdKey(ref Message msg, Keys keyData)

public bool AllowClose()
{
if (Conflicts != null)
if (Conflicts != null && Conflicts.Any())
{
if (Conflicts.Any())
{
// Ask if they want to resolve conflicts
string confDescrip = Conflicts
.Select(kvp => kvp.Value)
.Aggregate((a, b) => $"{a}, {b}");
if (!Main.Instance.YesNoDialog(string.Format(Properties.Resources.MainQuitWithConflicts, confDescrip),
Properties.Resources.MainQuit,
Properties.Resources.MainGoBack))
{
return false;
}
}
else
// Ask if they want to resolve conflicts
string confDescrip = Conflicts
.Select(kvp => kvp.Value)
.Aggregate((a, b) => $"{a}, {b}");
if (!Main.Instance.YesNoDialog(string.Format(Properties.Resources.MainQuitWithConflicts, confDescrip),
Properties.Resources.MainQuit,
Properties.Resources.MainGoBack))
{
// The Conflicts dictionary is empty even when there are unmet dependencies.
if (!Main.Instance.YesNoDialog(Properties.Resources.MainQuitWithUnmetDeps,
Properties.Resources.MainQuit,
Properties.Resources.MainGoBack))
{
return false;
}
return false;
}
}
else if (ChangeSet?.Any() ?? false)
Expand Down Expand Up @@ -1654,42 +1635,25 @@ public async Task UpdateChangeSetAndConflicts(GameInstance inst, IRegistryQuerie
var user_change_set = mainModList.ComputeUserChangeSet(registry, inst.VersionCriteria());
try
{
var gameVersion = inst.VersionCriteria();
var module_installer = new ModuleInstaller(inst, Main.Instance.Manager.Cache, Main.Instance.currentUser);
full_change_set = mainModList.ComputeChangeSetFromModList(registry, user_change_set, module_installer, inst.VersionCriteria()).ToList();
}
catch (InconsistentKraken k)
{
// Need to be recomputed due to ComputeChangeSetFromModList possibly changing it with too many provides handling.
Main.Instance.AddStatusMessage(k.ShortDescription);
user_change_set = mainModList.ComputeUserChangeSet(registry, inst.VersionCriteria());
new_conflicts = ModList.ComputeConflictsFromModList(registry, user_change_set, inst.VersionCriteria());
full_change_set = null;
}
catch (TooManyModsProvideKraken)
{
// Can be thrown by ComputeChangeSetFromModList if the user cancels out of it.
// We can just rerun it as the ModInfo has been removed.
too_many_provides_thrown = true;
var tuple = mainModList.ComputeFullChangeSetFromUserChangeSet(registry, user_change_set, module_installer, gameVersion);
full_change_set = tuple.Item1.ToList();
new_conflicts = tuple.Item2.ToDictionary(
item => new GUIMod(item.Key, registry, gameVersion),
item => item.Value);
Main.Instance.AddStatusMessage(string.Join(";", new_conflicts.Values));
}
catch (DependencyNotSatisfiedKraken k)
{
Main.Instance.currentUser.RaiseError(
Properties.Resources.MainDepNotSatisfied,
k.parent,
k.module
);
k.parent, k.module);

// Uncheck the box
MarkModForInstall(k.parent.identifier, true);
}

if (too_many_provides_thrown)
{
await UpdateChangeSetAndConflicts(inst, registry);
new_conflicts = Conflicts;
full_change_set = ChangeSet;
}

Conflicts = new_conflicts;
ChangeSet = full_change_set;
}
Expand Down
8 changes: 5 additions & 3 deletions GUI/Main/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -701,12 +701,14 @@ private void ShowSelectionModInfo(ListView.SelectedListViewItemCollection select
);
}

private void ManageMods_OnChangeSetChanged(List<ModChange> changeset)
private void ManageMods_OnChangeSetChanged(List<ModChange> changeset, Dictionary<GUIMod, string> conflicts)
{
if (changeset != null && changeset.Any())
{
tabController.ShowTab("ChangesetTabPage", 1, false);
UpdateChangesDialog(changeset);
UpdateChangesDialog(
changeset,
conflicts.ToDictionary(item => item.Key.ToCkanModule(), item => item.Value));
auditRecommendationsMenuItem.Enabled = false;
}
else
Expand Down Expand Up @@ -862,7 +864,7 @@ private void GameExit(GameInstance inst)
// This is used by Reinstall
private void ManageMods_StartChangeSet(List<ModChange> changeset)
{
UpdateChangesDialog(changeset);
UpdateChangesDialog(changeset, null);
tabController.ShowTab("ChangesetTabPage", 1);
}

Expand Down
7 changes: 4 additions & 3 deletions GUI/Main/MainChangeset.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ namespace CKAN.GUI
{
public partial class Main
{
private void UpdateChangesDialog(List<ModChange> changeset)
private void UpdateChangesDialog(List<ModChange> changeset, Dictionary<CkanModule, string> conflicts)
{
Changeset.LoadChangeset(
changeset,
ManageMods.mainModList.ModuleLabels.LabelsFor(CurrentInstance.Name)
.Where(l => l.AlertOnInstall)
.ToList());
.ToList(),
conflicts);
}

private void Changeset_OnSelectedItemsChanged(ListView.SelectedListViewItemCollection items)
Expand All @@ -26,7 +27,7 @@ private void Changeset_OnCancelChanges(bool reset)
if (reset)
{
ManageMods.ClearChangeSet();
UpdateChangesDialog(null);
UpdateChangesDialog(null, null);
}
tabController.ShowTab("ManageModsTabPage");
}
Expand Down
90 changes: 21 additions & 69 deletions GUI/Model/ModList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,22 @@ public static SavedSearch FilterToSavedSearch(GUIModFilter filter, ModuleTag tag
};
}

private static readonly RelationshipResolverOptions conflictOptions = new RelationshipResolverOptions()
{
without_toomanyprovides_kraken = true,
proceed_with_inconsistencies = true,
without_enforce_consistency = true,
with_recommends = false
};

/// <summary>
/// This function returns a changeset based on the selections of the user.
/// Currently returns null if a conflict is detected.
/// Returns a changeset and conflicts based on the selections of the user.
/// </summary>
/// <param name="registry"></param>
/// <param name="changeSet"></param>
/// <param name="installer">A module installer for the current game instance</param>
/// <param name="version">The version of the current game instance</param>
public IEnumerable<ModChange> ComputeChangeSetFromModList(
public Tuple<IEnumerable<ModChange>, Dictionary<CkanModule, string>> ComputeFullChangeSetFromUserChangeSet(
IRegistryQuerier registry, HashSet<ModChange> changeSet, ModuleInstaller installer,
GameVersionCriteria version)
{
Expand Down Expand Up @@ -189,24 +196,20 @@ public IEnumerable<ModChange> ComputeChangeSetFromModList(
}

// Get as many dependencies as we can, but leave decisions and prompts for installation time
RelationshipResolverOptions opts = RelationshipResolver.DependsOnlyOpts();
opts.without_toomanyprovides_kraken = true;
opts.without_enforce_consistency = true;

var resolver = new RelationshipResolver(
modules_to_install,
modules_to_remove,
opts, registry, version);
modules_to_install, modules_to_remove,
conflictOptions, registry, version);

// Replace Install entries in changeset with the ones from resolver to get all the reasons
return changeSet
.Where(ch => !(ch.ChangeType is GUIModChangeType.Install))
.OrderBy(ch => ch.Mod.identifier)
.Union(resolver.ModList()
// Changeset already contains Update changes for these
.Except(upgrading)
.Where(m => !m.IsMetapackage)
.Select(m => new ModChange(m, GUIModChangeType.Install, resolver.ReasonsFor(m))));
return new Tuple<IEnumerable<ModChange>, Dictionary<CkanModule, string>>(
changeSet.Where(ch => !(ch.ChangeType is GUIModChangeType.Install))
.OrderBy(ch => ch.Mod.identifier)
.Union(resolver.ModList()
// Changeset already contains Update changes for these
.Except(upgrading)
.Where(m => !m.IsMetapackage)
.Select(m => new ModChange(m, GUIModChangeType.Install, resolver.ReasonsFor(m)))),
resolver.ConflictList);
}

/// <summary>
Expand Down Expand Up @@ -461,57 +464,6 @@ public string StripEpoch(string version)
private static readonly Regex ContainsEpoch = new Regex(@"^[0-9][0-9]*:[^:]+$", RegexOptions.Compiled);
private static readonly Regex RemoveEpoch = new Regex(@"^([^:]+):([^:]+)$", RegexOptions.Compiled);

public static Dictionary<GUIMod, string> ComputeConflictsFromModList(IRegistryQuerier registry,
IEnumerable<ModChange> change_set, GameVersionCriteria ksp_version)
{
var modules_to_install = new HashSet<CkanModule>();
var modules_to_remove = new HashSet<CkanModule>();
var options = new RelationshipResolverOptions
{
without_toomanyprovides_kraken = true,
proceed_with_inconsistencies = true,
without_enforce_consistency = true,
with_recommends = false
};

foreach (var change in change_set)
{
switch (change.ChangeType)
{
case GUIModChangeType.None:
break;
case GUIModChangeType.Install:
modules_to_install.Add(change.Mod);
break;
case GUIModChangeType.Remove:
modules_to_remove.Add(change.Mod);
break;
case GUIModChangeType.Update:
break;
case GUIModChangeType.Replace:
ModuleReplacement repl = registry.GetReplacement(change.Mod, ksp_version);
if (repl != null)
{
modules_to_remove.Add(repl.ToReplace);
modules_to_install.Add(repl.ReplaceWith);
}
break;
default:
throw new ArgumentOutOfRangeException();
}
}

var resolver = new RelationshipResolver(
modules_to_install.Except(modules_to_remove),
modules_to_remove,
options, registry, ksp_version
);
return resolver.ConflictList.ToDictionary(
item => new GUIMod(item.Key, registry, ksp_version),
item => item.Value
);
}

public HashSet<ModChange> ComputeUserChangeSet(IRegistryQuerier registry, GameVersionCriteria crit)
{
log.Debug("Computing user changeset");
Expand Down
2 changes: 1 addition & 1 deletion Tests/GUI/Model/ModList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ namespace Tests.GUI
public class ModListTests
{
[Test]
public void ComputeChangeSetFromModList_WithEmptyList_HasEmptyChangeSet()
public void ComputeFullChangeSetFromUserChangeSet_WithEmptyList_HasEmptyChangeSet()
{
var item = new ModList(delegate { });
Assert.That(item.ComputeUserChangeSet(null, null), Is.Empty);
Expand Down

0 comments on commit 2fc0dbb

Please sign in to comment.