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

Usability improvements for missing files #4195

Merged
merged 2 commits into from
Sep 26, 2024
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
18 changes: 9 additions & 9 deletions Core/ModuleInstaller.cs
Original file line number Diff line number Diff line change
Expand Up @@ -584,21 +584,21 @@ public static List<InstallableFile> FindInstallableFiles(CkanModule module, stri

/// <summary>
/// Returns the module contents if and only if we have it
/// available in our cache. Returns null, otherwise.
/// available in our cache, empty sequence otherwise.
///
/// Intended for previews.
/// </summary>
public static IEnumerable<string> GetModuleContentsList(NetModuleCache Cache,
GameInstance instance,

CkanModule module)
public static IEnumerable<InstallableFile> GetModuleContents(NetModuleCache Cache,
GameInstance instance,
CkanModule module,
HashSet<string> filters)
=> (Cache.GetCachedFilename(module) is string filename
? Utilities.DefaultIfThrows(() => FindInstallableFiles(module, filename, instance)
// Skip folders
.Where(f => !f.source.IsDirectory)
.Select(f => instance.ToRelativeGameDir(f.destination)))
.Where(instF => !filters.Any(filt =>
instF.destination != null
&& instF.destination.Contains(filt))))
: null)
?? Enumerable.Empty<string>();
?? Enumerable.Empty<InstallableFile>();

#endregion

Expand Down
2 changes: 1 addition & 1 deletion Core/Registry/IRegistryQuerier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ public static Dictionary<bool, List<CkanModule>> CheckUpgradeable(this IRegistry
};
}

private static bool MetadataChanged(this IRegistryQuerier querier, string identifier)
public static bool MetadataChanged(this IRegistryQuerier querier, string identifier)
{
try
{
Expand Down
2 changes: 1 addition & 1 deletion GUI/Controls/ManageMods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1291,7 +1291,7 @@ private void reinstallToolStripMenuItem_Click(object? sender, EventArgs? e)
registry.GetModuleByVersion(module.identifier,
module.version)
?? module,
true)
true, false)
}, null);
}
}
Expand Down
1 change: 1 addition & 0 deletions GUI/Controls/ModInfoTabs/Contents.Designer.cs

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

79 changes: 56 additions & 23 deletions GUI/Controls/ModInfoTabs/Contents.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
Expand All @@ -9,6 +8,9 @@
using System.Runtime.Versioning;
#endif

using Autofac;

using CKAN.Configuration;
using CKAN.GUI.Attributes;

namespace CKAN.GUI
Expand Down Expand Up @@ -53,9 +55,9 @@ public void RefreshModContentsTree()

private void ContentsPreviewTree_NodeMouseDoubleClick(object? sender, TreeNodeMouseClickEventArgs? e)
{
if (e != null)
if (e != null && manager?.CurrentInstance is GameInstance inst)
{
Utilities.OpenFileBrowser(e.Node.Name);
Utilities.OpenFileBrowser(inst.ToAbsoluteGameDir(e.Node.Name));
}
}

Expand Down Expand Up @@ -119,9 +121,9 @@ private void _UpdateModContentsTree(CkanModule? module, bool force = false)
ContentsOpenButton.Enabled = false;
ContentsPreviewTree.Enabled = false;
}
else if (manager != null
&& manager?.CurrentInstance != null
else if (manager?.CurrentInstance is GameInstance inst
&& manager?.Cache != null
&& selectedModule != null
&& Main.Instance?.currentUser != null)
{
rootNode.Text = Path.GetFileName(
Expand All @@ -134,23 +136,33 @@ private void _UpdateModContentsTree(CkanModule? module, bool force = false)
UseWaitCursor = true;
Task.Factory.StartNew(() =>
{
var paths = ModuleInstaller.GetModuleContentsList(manager.Cache, manager.CurrentInstance, module)
// Load fully in bg
.ToArray();
var filters = ServiceLocator.Container.Resolve<IConfiguration>().GlobalInstallFilters
.Concat(inst.InstallFilters)
.ToHashSet();
var tuples = ModuleInstaller.GetModuleContents(manager.Cache, inst, module, filters)
.Select(f => (path: inst.ToRelativeGameDir(f.destination),
dir: f.source.IsDirectory,
exists: !selectedModule.IsInstalled
|| File.Exists(f.destination)
|| Directory.Exists(f.destination)))
.ToArray();
// Stop if user switched to another mod
if (rootNode.TreeView != null)
{
Util.Invoke(this, () =>
{
ContentsPreviewTree.BeginUpdate();
foreach (string path in paths)
foreach ((string path, bool dir, bool exists) in tuples)
{
AddContentPieces(
rootNode,
path.Split(new char[] {'/'}));
AddContentPieces(inst, rootNode,
path.Split(new char[] {'/'}),
dir, exists);
}
rootNode.ExpandAll();
rootNode.EnsureVisible();
var initialFocus = FirstMatching(rootNode,
n => n.ForeColor == Color.Red)
?? rootNode;
initialFocus.EnsureVisible();
ContentsPreviewTree.EndUpdate();
UseWaitCursor = false;
});
Expand All @@ -161,24 +173,45 @@ private void _UpdateModContentsTree(CkanModule? module, bool force = false)
}
}

private static void AddContentPieces(TreeNode parent, IEnumerable<string> pieces)
private static void AddContentPieces(GameInstance inst,
TreeNode parent,
string[] pieces,
bool dir,
bool exists)
{
var firstPiece = pieces.FirstOrDefault();
if (firstPiece != null)
{
if (parent.ImageKey == "file")
{
parent.SelectedImageKey = parent.ImageKey = "folder";
}
// Key/Name needs to be the full relative path for double click to work
var key = string.IsNullOrEmpty(parent.Name)
? firstPiece
: $"{parent.Name}/{firstPiece}";
var node = parent.Nodes[key]
?? parent.Nodes.Add(key, firstPiece, "file", "file");
AddContentPieces(node, pieces.Skip(1));
? firstPiece
: $"{parent.Name}/{firstPiece}";
var node = parent.Nodes[key];
if (node == null)
{
var iconKey = dir || pieces.Length > 1 ? "folder" : "file";
node = parent.Nodes.Add(key, firstPiece, iconKey, iconKey);
if (!exists && (pieces.Length == 1 || !Directory.Exists(inst.ToAbsoluteGameDir(key))))
{
node.ForeColor = Color.Red;
node.ToolTipText = iconKey == "folder"
? Properties.Resources.ModInfoFolderNotFound
: Properties.Resources.ModInfoFileNotFound;
}
}
if (pieces.Length > 1)
{
AddContentPieces(inst, node, pieces.Skip(1).ToArray(), dir, exists);
}
}
}

private static TreeNode? FirstMatching(TreeNode root, Func<TreeNode, bool> predicate)
=> predicate(root) ? root
: root.Nodes.OfType<TreeNode>()
.Select(n => FirstMatching(n, predicate))
.OfType<TreeNode>()
.FirstOrDefault();

}
}
8 changes: 5 additions & 3 deletions GUI/Model/GUIMod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,9 @@ public CkanModule ToCkanModule()
/// <returns>The CkanModule associated with this GUIMod or null if there is none</returns>
public CkanModule ToModule() => Mod;

public IEnumerable<ModChange> GetModChanges(bool upgradeChecked, bool replaceChecked)
public IEnumerable<ModChange> GetModChanges(bool upgradeChecked,
bool replaceChecked,
bool metadataChanged)
{
if (replaceChecked)
{
Expand All @@ -309,7 +311,7 @@ public IEnumerable<ModChange> GetModChanges(bool upgradeChecked, bool replaceChe
yield return new ModUpgrade(Mod,
GUIModChangeType.Update,
SelectedMod,
false);
false, false);
}
else
{
Expand All @@ -329,7 +331,7 @@ public IEnumerable<ModChange> GetModChanges(bool upgradeChecked, bool replaceChe
yield return new ModUpgrade(Mod,
GUIModChangeType.Update,
SelectedMod,
false);
false, metadataChanged);
}
}

Expand Down
14 changes: 9 additions & 5 deletions GUI/Model/ModChange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -138,20 +138,23 @@ public class ModUpgrade : ModChange
public ModUpgrade(CkanModule mod,
GUIModChangeType changeType,
CkanModule targetMod,
bool userReinstall)
bool userReinstall,
bool metadataChanged)
: base(mod, changeType)
{
this.targetMod = targetMod;
this.userReinstall = userReinstall;
this.targetMod = targetMod;
this.userReinstall = userReinstall;
this.metadataChanged = metadataChanged;
}

public override string? NameAndStatus
=> Main.Instance?.Manager?.Cache?.DescribeAvailability(targetMod);

public override string Description
=> IsReinstall
? userReinstall ? Properties.Resources.MainChangesetUserReinstall
: Properties.Resources.MainChangesetReinstall
? userReinstall ? Properties.Resources.MainChangesetReinstallUser
: metadataChanged ? Properties.Resources.MainChangesetReinstallMetadataChanged
: Properties.Resources.MainChangesetReinstallMissing
: string.Format(Properties.Resources.MainChangesetUpdateSelected,
targetMod.version);

Expand All @@ -165,5 +168,6 @@ private bool IsReinstall
&& targetMod.version == Mod.version;

private readonly bool userReinstall;
private readonly bool metadataChanged;
}
}
18 changes: 10 additions & 8 deletions GUI/Model/ModList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -434,27 +434,29 @@ 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);

private static IEnumerable<ModChange> rowChanges(DataGridViewRow row,
DataGridViewColumn? upgradeCol,
DataGridViewColumn? replaceCol)
=> (row.Tag as GUIMod)?.GetModChanges(
private static IEnumerable<ModChange> rowChanges(IRegistryQuerier registry,
DataGridViewRow row,
DataGridViewColumn? upgradeCol,
DataGridViewColumn? replaceCol)
=> row.Tag is GUIMod gmod ? gmod.GetModChanges(
upgradeCol != null && upgradeCol.Visible
&& row.Cells[upgradeCol.Index] is DataGridViewCheckBoxCell upgradeCell
&& (bool)upgradeCell.Value,
replaceCol != null && replaceCol.Visible
&& row.Cells[replaceCol.Index] is DataGridViewCheckBoxCell replaceCell
&& (bool)replaceCell.Value)
?? Enumerable.Empty<ModChange>();
&& (bool)replaceCell.Value,
registry.MetadataChanged(gmod.Identifier))
: Enumerable.Empty<ModChange>();

public HashSet<ModChange> ComputeUserChangeSet(IRegistryQuerier? registry,
public HashSet<ModChange> ComputeUserChangeSet(IRegistryQuerier registry,
GameVersionCriteria? crit,
GameInstance? instance,
DataGridViewColumn? upgradeCol,
DataGridViewColumn? replaceCol)
{
log.Debug("Computing user changeset");
var modChanges = full_list_of_mod_rows?.Values
.SelectMany(row => rowChanges(row, upgradeCol, replaceCol))
.SelectMany(row => rowChanges(registry, row, upgradeCol, replaceCol))
.ToList()
?? new List<ModChange>();

Expand Down
2 changes: 1 addition & 1 deletion GUI/Properties/Resources.de-DE.resx
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ Möchten Sie es wirklich installieren?</value></data>
<data name="AllModVersionsInstallYes" xml:space="preserve"><value>Installieren</value></data>
<data name="AllModVersionsInstallNo" xml:space="preserve"><value>Abbrechen</value></data>
<data name="MainChangesetUpdateSelected" xml:space="preserve"><value>Mod-Update ausgewählt vom Nutzer {0}.</value></data>
<data name="MainChangesetReinstall" xml:space="preserve"><value>Neu installieren (Metadaten geändert)</value></data>
<data name="MainChangesetReinstallMetadataChanged" xml:space="preserve"><value>Neu installieren (Metadaten geändert)</value></data>
<data name="MainImportTitle" xml:space="preserve"><value>Mod-Import</value></data>
<data name="MainImportWaitTitle" xml:space="preserve"><value>Statuslog</value></data>
<data name="MainInstallWaitTitle" xml:space="preserve"><value>Statuslog</value></data>
Expand Down
4 changes: 2 additions & 2 deletions GUI/Properties/Resources.fr-FR.resx
Original file line number Diff line number Diff line change
Expand Up @@ -359,10 +359,10 @@ Voulez-vous vraiment les installer ? L'annulation annulera toute l'installation.
<data name="MainChangesetUpdateSelected" xml:space="preserve">
<value>Mise à jour demandée vers la version {0}.</value>
</data>
<data name="MainChangesetReinstall" xml:space="preserve">
<data name="MainChangesetReinstallMetadataChanged" xml:space="preserve">
<value>Réinstaller (métadonnées modifiées)</value>
</data>
<data name="MainChangesetUserReinstall" xml:space="preserve">
<data name="MainChangesetReinstallUser" xml:space="preserve">
<value>Réinstallation (à la demande de l'utilisateur)</value>
</data>
<data name="MainImportTitle" xml:space="preserve">
Expand Down
2 changes: 1 addition & 1 deletion GUI/Properties/Resources.it-IT.resx
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ Sei sicuro di volerli installare? L'annullamento interromperà l'intera installa
<data name="MainChangesetUpdateSelected" xml:space="preserve">
<value>Aggiorna la versione selezionata dall'utente alla versione {0}.</value>
</data>
<data name="MainChangesetReinstall" xml:space="preserve">
<data name="MainChangesetReinstallMetadataChanged" xml:space="preserve">
<value>Reinstallazione (Metadati cambiati)</value>
</data>
<data name="MainImportTitle" xml:space="preserve">
Expand Down
2 changes: 1 addition & 1 deletion GUI/Properties/Resources.ja-JP.resx
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ Try to move {2} out of {3} and restart CKAN.</value></data>
<data name="AllModVersionsInstallYes" xml:space="preserve"><value>インストール</value></data>
<data name="AllModVersionsInstallNo" xml:space="preserve"><value>取消</value></data>
<data name="MainChangesetUpdateSelected" xml:space="preserve"><value>選択されたものをバージョン{0}にアップデートする。</value></data>
<data name="MainChangesetReinstall" xml:space="preserve"><value> ()</value></data>
<data name="MainChangesetReinstallMetadataChanged" xml:space="preserve"><value> ()</value></data>
<data name="MainImportTitle" xml:space="preserve"><value>Modインポート</value></data>
<data name="MainImportFilter" xml:space="preserve"><value>Mod (*.zip)|*.zip</value></data>
<data name="MainImportWaitTitle" xml:space="preserve"><value>ステータスログ</value></data>
Expand Down
2 changes: 1 addition & 1 deletion GUI/Properties/Resources.ko-KR.resx
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@
<data name="AllModVersionsInstallYes" xml:space="preserve"><value>설치</value></data>
<data name="AllModVersionsInstallNo" xml:space="preserve"><value>취소하기</value></data>
<data name="MainChangesetUpdateSelected" xml:space="preserve"><value>유저가 선택한 것을 버전 {0}으로 업데이트 하기.</value></data>
<data name="MainChangesetReinstall" xml:space="preserve"><value> ()</value></data>
<data name="MainChangesetReinstallMetadataChanged" xml:space="preserve"><value> ()</value></data>
<data name="MainChangesetNewInstall" xml:space="preserve"><value>유저가 선택한 모드를 설치하기.</value></data>
<data name="MainImportTitle" xml:space="preserve"><value>모드들 가져오기</value></data>
<data name="MainImportFilter" xml:space="preserve"><value>모드들 (*.zip)|*.zip</value></data>
Expand Down
Loading