Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show conflicts in changeset #3727

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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