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

ConsoleUI prompt to delete non-empty folders after uninstall #4066

Merged
merged 1 commit into from
Mar 23, 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
2 changes: 1 addition & 1 deletion Cmdline/Action/List.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public int RunCommand(CKAN.GameInstance instance, object raw_options)
user.RaiseMessage("");
user.RaiseMessage(Properties.Resources.ListGameFound,
instance.game.ShortName,
instance.GameDir().Replace('/', Path.DirectorySeparatorChar));
Platform.FormatPath(instance.GameDir()));
user.RaiseMessage("");
user.RaiseMessage(Properties.Resources.ListGameVersion, instance.game.ShortName, instance.Version());
user.RaiseMessage("");
Expand Down
2 changes: 1 addition & 1 deletion ConsoleUI/AuthTokenListScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public AuthTokenScreen() : base()
},
new ConsoleListBoxColumn<string>() {
Header = Properties.Resources.AuthTokenListTokenHeader,
Width = 50,
Width = null,
Renderer = (string s) => {
return ServiceLocator.Container.Resolve<IConfiguration>().TryGetAuthToken(s, out string token)
? token
Expand Down
2 changes: 1 addition & 1 deletion ConsoleUI/CompatibleVersionDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public CompatibleVersionDialog(IGame game) : base()
new List<ConsoleListBoxColumn<GameVersion>>() {
new ConsoleListBoxColumn<GameVersion>() {
Header = Properties.Resources.CompatibleVersionsListHeader,
Width = r - l - 5,
Width = null,
Renderer = v => v.ToString(),
Comparer = (v1, v2) => v1.CompareTo(v2)
}
Expand Down
122 changes: 122 additions & 0 deletions ConsoleUI/DeleteDirectoriesScreen.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using System;
using System.Linq;
using System.IO;
using System.Collections.Generic;

using CKAN.ConsoleUI.Toolkit;

namespace CKAN.ConsoleUI {
/// <summary>
/// Screen prompting user to delete game-created folders
/// </summary>
public class DeleteDirectoriesScreen : ConsoleScreen {

/// <summary>
/// Initialize the screen
/// </summary>
/// <param name="inst">Game instance</param>
/// <param name="possibleConfigOnlyDirs">Deletable stuff the user should see</param>
public DeleteDirectoriesScreen(GameInstance inst,
HashSet<string> possibleConfigOnlyDirs)
{
instance = inst;
toDelete = possibleConfigOnlyDirs.ToHashSet();

var tb = new ConsoleTextBox(1, 2, -1, 4, false);
tb.AddLine(Properties.Resources.DeleteDirectoriesHelpLabel);
AddObject(tb);

var listWidth = (Console.WindowWidth - 4) / 2;

directories = new ConsoleListBox<string>(
1, 6, listWidth - 1, -2,
possibleConfigOnlyDirs.OrderBy(d => d).ToList(),
new List<ConsoleListBoxColumn<string>>() {
new ConsoleListBoxColumn<string>() {
Header = "",
Width = 1,
Renderer = s => toDelete.Contains(s) ? Symbols.checkmark : "",
},
new ConsoleListBoxColumn<string>() {
Header = Properties.Resources.DeleteDirectoriesFoldersHeader,
Width = null,
// The data model holds absolute paths, but the UI shows relative
Renderer = p => Platform.FormatPath(instance.ToRelativeGameDir(p)),
},
},
0, -1);
directories.AddTip("D", Properties.Resources.DeleteDirectoriesDeleteDirTip,
() => !toDelete.Contains(directories.Selection));
directories.AddBinding(Keys.D, (object sender, ConsoleTheme theme) => {
toDelete.Add(directories.Selection);
return true;
});
directories.AddTip("K", Properties.Resources.DeleteDirectoriesKeepDirTip,
() => toDelete.Contains(directories.Selection));
directories.AddBinding(Keys.K, (object sender, ConsoleTheme theme) => {
toDelete.Remove(directories.Selection);
return true;
});
directories.SelectionChanged += PopulateFiles;
AddObject(directories);

files = new ConsoleListBox<string>(
listWidth + 1, 6, -1, -2,
new List<string>() { "" },
new List<ConsoleListBoxColumn<string>>() {
new ConsoleListBoxColumn<string>() {
Header = Properties.Resources.DeleteDirectoriesFilesHeader,
Width = null,
Renderer = p => Platform.FormatPath(CKANPathUtils.ToRelative(p, directories.Selection)),
},
},
0, -1);
AddObject(files);

AddTip(Properties.Resources.Esc,
Properties.Resources.DeleteDirectoriesCancelTip);
AddBinding(Keys.Escape,
// Discard changes
(object sender, ConsoleTheme theme) => false);

AddTip("F9", Properties.Resources.DeleteDirectoriesApplyTip);
AddBinding(Keys.F9, (object sender, ConsoleTheme theme) => {
foreach (var d in toDelete) {
try {
Directory.Delete(d, true);
} catch {
}
}
return false;
});

PopulateFiles();
}

private void PopulateFiles()
{
files.SetData(
Directory.EnumerateFileSystemEntries(directories.Selection,
"*",
SearchOption.AllDirectories)
.OrderBy(f => f)
.ToList(),
true);
}

/// <summary>
/// Put CKAN 1.25.5 in top left corner
/// </summary>
protected override string LeftHeader() => $"CKAN {Meta.GetVersion()}";

/// <summary>
/// Show the Delete Directories header
/// </summary>
protected override string CenterHeader() => Properties.Resources.DeleteDirectoriesHeader;

private readonly GameInstance instance;
private readonly HashSet<string> toDelete;
private readonly ConsoleListBox<string> directories;
private readonly ConsoleListBox<string> files;
}
}
2 changes: 1 addition & 1 deletion ConsoleUI/DependencyScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public DependencyScreen(GameInstanceManager mgr, Registry registry, ChangePlan c
},
new ConsoleListBoxColumn<Dependency>() {
Header = Properties.Resources.RecommendationsNameHeader,
Width = 36,
Width = null,
Renderer = (Dependency d) => d.module.ToString()
},
new ConsoleListBoxColumn<Dependency>() {
Expand Down
2 changes: 1 addition & 1 deletion ConsoleUI/GameInstanceEditScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public GameInstanceEditScreen(GameInstanceManager mgr, RepositoryDataManager rep
}, new ConsoleListBoxColumn<Repository>() {
Header = Properties.Resources.InstanceEditRepoURLHeader,
Renderer = r => r.uri.ToString(),
Width = 50
Width = null
}
},
1, 0, ListSortDirection.Ascending
Expand Down
4 changes: 2 additions & 2 deletions ConsoleUI/GameInstanceListScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public GameInstanceListScreen(GameInstanceManager mgr, RepositoryDataManager rep
Comparer = (a, b) => a.Version()?.CompareTo(b.Version() ?? GameVersion.Any) ?? 1
}, new ConsoleListBoxColumn<GameInstance>() {
Header = Properties.Resources.InstanceListPathHeader,
Width = 70,
Width = null,
Renderer = k => k.GameDir()
}
},
Expand Down Expand Up @@ -229,7 +229,7 @@ public static bool TryGetInstance(ConsoleTheme theme,

ConsoleMessageDialog errd = new ConsoleMessageDialog(
string.Format(Properties.Resources.InstanceListLoadingError,
Path.Combine(ksp.CkanDir(), "registry.json").Replace('/', Path.DirectorySeparatorChar),
Platform.FormatPath(Path.Combine(ksp.CkanDir(), "registry.json")),
e.ToString()),
new List<string>() { Properties.Resources.OK }
);
Expand Down
4 changes: 2 additions & 2 deletions ConsoleUI/InstallFiltersScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public InstallFiltersScreen(IConfiguration globalConfig, GameInstance instance)
new List<ConsoleListBoxColumn<string>>() {
new ConsoleListBoxColumn<string>() {
Header = Properties.Resources.FiltersGlobalHeader,
Width = 40,
Width = null,
Renderer = f => f,
}
},
Expand All @@ -73,7 +73,7 @@ public InstallFiltersScreen(IConfiguration globalConfig, GameInstance instance)
new List<ConsoleListBoxColumn<string>>() {
new ConsoleListBoxColumn<string>() {
Header = Properties.Resources.FiltersInstanceHeader,
Width = 40,
Width = null,
Renderer = f => f,
}
},
Expand Down
23 changes: 23 additions & 0 deletions ConsoleUI/InstallScreen.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.IO;
using System.Transactions;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -105,6 +106,8 @@ public override void Run(ConsoleTheme theme, Action<ConsoleTheme> process = null
// Don't let the installer re-use old screen references
inst.User = null;

HandlePossibleConfigOnlyDirs(theme, registry, possibleConfigOnlyDirs);

} catch (CancelledActionKraken) {
// Don't need to tell the user they just cancelled out.
} catch (FileNotFoundKraken ex) {
Expand Down Expand Up @@ -165,6 +168,26 @@ public override void Run(ConsoleTheme theme, Action<ConsoleTheme> process = null
}
}

private void HandlePossibleConfigOnlyDirs(ConsoleTheme theme,
Registry registry,
HashSet<string> possibleConfigOnlyDirs)
{
if (possibleConfigOnlyDirs != null)
{
// Check again for registered files, since we may
// just have installed or upgraded some
possibleConfigOnlyDirs.RemoveWhere(
d => !Directory.Exists(d)
|| Directory.EnumerateFileSystemEntries(d, "*", SearchOption.AllDirectories)
.Select(absF => manager.CurrentInstance.ToRelativeGameDir(absF))
.Any(relF => registry.FileOwner(relF) != null));
if (possibleConfigOnlyDirs.Count > 0)
{
LaunchSubScreen(theme, new DeleteDirectoriesScreen(manager.CurrentInstance, possibleConfigOnlyDirs));
}
}
}

private void OnModInstalled(CkanModule mod)
{
RaiseMessage(Properties.Resources.InstallModInstalled, Symbols.checkmark, mod.name, ModuleInstaller.StripEpoch(mod.version));
Expand Down
9 changes: 6 additions & 3 deletions ConsoleUI/ModListScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public ModListScreen(GameInstanceManager mgr, RepositoryDataManager repoData, Re
Renderer = StatusSymbol
}, new ConsoleListBoxColumn<CkanModule>() {
Header = Properties.Resources.ModListNameHeader,
Width = 44,
Width = null,
Renderer = m => m.name ?? ""
}, new ConsoleListBoxColumn<CkanModule>() {
Header = Properties.Resources.ModListVersionHeader,
Expand Down Expand Up @@ -639,8 +639,11 @@ private bool Help(ConsoleTheme theme)

private bool ApplyChanges(ConsoleTheme theme)
{
LaunchSubScreen(theme, new InstallScreen(manager, repoData, plan, debug));
RefreshList(theme);
if (plan.NonEmpty())
{
LaunchSubScreen(theme, new InstallScreen(manager, repoData, plan, debug));
RefreshList(theme);
}
return true;
}

Expand Down
6 changes: 6 additions & 0 deletions ConsoleUI/Properties/Resources.fr-FR.resx
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,12 @@ Veuillez désinstaller manuellement le mod auquel ce fichier appartient pour pou
<data name="InstallModInstalled" xml:space="preserve">
<value>{0} installé avec succès {1} {2}</value>
</data>
<data name="DeleteDirectoriesHeader" xml:space="preserve"><value>Supprimer les Dossiers</value></data>
<data name="DeleteDirectoriesHelpLabel" xml:space="preserve"><value>Attention, il reste certains dossiers, parce que CKAN ne sait pas s'il peut supprimer les fichiers restants. Garder ces dossiers peut casser d'autres mods ! Si vous n'avez pas besoin de ces fichiers, il est recommandé de les supprimer.</value></data>
<data name="DeleteDirectoriesFoldersHeader" xml:space="preserve"><value>Dossiers</value></data>
<data name="DeleteDirectoriesFilesHeader" xml:space="preserve"><value>Contenu du Dossier</value></data>
<data name="DeleteDirectoriesCancelTip" xml:space="preserve"><value>Tout Garder</value></data>
<data name="DeleteDirectoriesApplyTip" xml:space="preserve"><value>Supprimer Coché</value></data>
<data name="ModInfoTitle" xml:space="preserve">
<value>Détails du Mod</value>
</data>
Expand Down
7 changes: 7 additions & 0 deletions ConsoleUI/Properties/Resources.it-IT.resx
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,13 @@ Modificaew i token di autenticazione adesso?</value>
<data name="InstallModInstalled" xml:space="preserve">
<value>{0} installato con successo {1} {2}</value>
</data>
<data name="DeleteDirectoriesHeader" xml:space="preserve"><value>Elimina Cartelle</value></data>
<data name="DeleteDirectoriesHelpLabel" xml:space="preserve"><value>Le directory sottostanti sono dei residui rimasti a seguito della rimozione di alcune mod. Contengono file che non sono stati installati da CKAN (probabilmente generati da una mod o installati manualmente). CKAN non elimina automaticamente i file che non ha installato, ma puoi scegliere di rimuoverli se ti sembra sicuro farlo (scelta consigliata).
Nota che se decidi di non rimuovere una cartella, ModuleManager potrebbe pensare erroneamente che il mod sia ancora installato.</value></data>
<data name="DeleteDirectoriesFoldersHeader" xml:space="preserve"><value>Cartelle</value></data>
<data name="DeleteDirectoriesFilesHeader" xml:space="preserve"><value>Contenuto Cartella</value></data>
<data name="DeleteDirectoriesCancelTip" xml:space="preserve"><value>Mantieni Tutte</value></data>
<data name="DeleteDirectoriesApplyTip" xml:space="preserve"><value>Elimina Selezionata</value></data>
<data name="ModInfoTitle" xml:space="preserve">
<value>Dettagli Mod</value>
</data>
Expand Down
7 changes: 7 additions & 0 deletions ConsoleUI/Properties/Resources.pl-PL.resx
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,13 @@ Edytować tokeny uwierzytelniania?</value>
<data name="InstallModInstalled" xml:space="preserve">
<value>{0} pomyślnie zainstalowano {1} {2}</value>
</data>
<data name="DeleteDirectoriesHeader" xml:space="preserve"><value>Usuń foldery</value></data>
<data name="DeleteDirectoriesHelpLabel" xml:space="preserve"><value>Poniższe foldery są pozostałościami po usuniętych modach. Zawierają pliki które nie były pobierane przez CKAN (prawdopodobnie wygenerowane przez moda bądź zainstalowane ręcznie). CKAN nie usuwa plików których nie instalował ale możesz sam je usunąć jeśli uważasz że to bezpieczne (zalecane)
Uważaj bo jeśli nie usuniesz folderu, ModuleManager może myśleć że mod jest nadal zainstalowany.</value></data>
<data name="DeleteDirectoriesFoldersHeader" xml:space="preserve"><value>Foldery</value></data>
<data name="DeleteDirectoriesFilesHeader" xml:space="preserve"><value>Zawartość folderu</value></data>
<data name="DeleteDirectoriesCancelTip" xml:space="preserve"><value>Zachowaj wszystko</value></data>
<data name="DeleteDirectoriesApplyTip" xml:space="preserve"><value>Usuń zaznaczone</value></data>
<data name="ModInfoTitle" xml:space="preserve">
<value>Szczegóły modyfikacji</value>
</data>
Expand Down
8 changes: 8 additions & 0 deletions ConsoleUI/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,14 @@ Edit authentication tokens now?</value></data>
{1}</value></data>
<data name="InstallNotInstalled" xml:space="preserve"><value>{0} is not installed, can't remove</value></data>
<data name="InstallModInstalled" xml:space="preserve"><value>{0} Successfully installed {1} {2}</value></data>
<data name="DeleteDirectoriesHeader" xml:space="preserve"><value>Delete Directories</value></data>
<data name="DeleteDirectoriesHelpLabel" xml:space="preserve"><value>Warning, some folders have been left behind because CKAN does not know whether it is safe to delete their remaining files. Keeping these folders may break other mods! If you do not need these files, deleting them is recommended.</value></data>
<data name="DeleteDirectoriesFoldersHeader" xml:space="preserve"><value>Directories</value></data>
<data name="DeleteDirectoriesFilesHeader" xml:space="preserve"><value>Directory Contents</value></data>
<data name="DeleteDirectoriesDeleteDirTip" xml:space="preserve"><value>Delete folder</value></data>
<data name="DeleteDirectoriesKeepDirTip" xml:space="preserve"><value>Keep folder</value></data>
<data name="DeleteDirectoriesCancelTip" xml:space="preserve"><value>Keep all</value></data>
<data name="DeleteDirectoriesApplyTip" xml:space="preserve"><value>Delete checked</value></data>
<data name="ModInfoTitle" xml:space="preserve"><value>Mod Details</value></data>
<data name="ModInfoMenuTip" xml:space="preserve"><value>Links</value></data>
<data name="ModInfoAuthors" xml:space="preserve"><value>By {0}</value></data>
Expand Down
7 changes: 7 additions & 0 deletions ConsoleUI/Properties/Resources.ru-RU.resx
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,13 @@
<data name="InstallModInstalled" xml:space="preserve">
<value>{0} Успешно установлен {1} {2}</value>
</data>
<data name="DeleteDirectoriesHeader" xml:space="preserve"><value>Удаление папок</value></data>
<data name="DeleteDirectoriesHelpLabel" xml:space="preserve"><value>Следующие папки остались после удаления модификаций. Они содержат файлы, не установленные CKAN (сгенерированные модификациями или установленные вручную). CKAN не удаляет сторонние файлы автоматически, однако вы можете удалить их вручную, если это безопасно.
Обратите внимание, что если папки не будут удалены, ModuleManager может неверно посчитать, что модификации установлены.</value></data>
<data name="DeleteDirectoriesFoldersHeader" xml:space="preserve"><value>Папки</value></data>
<data name="DeleteDirectoriesFilesHeader" xml:space="preserve"><value>Содержимое папок</value></data>
<data name="DeleteDirectoriesCancelTip" xml:space="preserve"><value>Оставить всё</value></data>
<data name="DeleteDirectoriesApplyTip" xml:space="preserve"><value>Удалить отмеченное</value></data>
<data name="ModInfoTitle" xml:space="preserve">
<value>О модификации</value>
</data>
Expand Down
2 changes: 1 addition & 1 deletion ConsoleUI/Toolkit/ConsoleChoiceDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public ConsoleChoiceDialog(string m, string hdr, List<ChoiceT> c, Func<ChoiceT,
new List<ConsoleListBoxColumn<ChoiceT>>() {
new ConsoleListBoxColumn<ChoiceT>() {
Header = hdr,
Width = w - 6,
Width = null,
Renderer = renderer,
Comparer = comparer
}
Expand Down
Loading
Loading