private void InstallMods(object sender, DoWorkEventArgs e) // this probably needs to be refactored { installCanceled = false; ClearLog(); var opts = (KeyValuePair <ModChanges, RelationshipResolverOptions>)e.Argument; IRegistryQuerier registry = RegistryManager.Instance(manager.CurrentInstance).registry; ModuleInstaller installer = ModuleInstaller.GetInstance(CurrentInstance, GUI.user); // setup progress callback toInstall = new HashSet <string>(); var toUninstall = new HashSet <string>(); var toUpgrade = new HashSet <string>(); // First compose sets of what the user wants installed, upgraded, and removed. foreach (ModChange change in opts.Key) { switch (change.ChangeType) { case GUIModChangeType.Remove: toUninstall.Add(change.Mod.Identifier); break; case GUIModChangeType.Update: toUpgrade.Add(change.Mod.Identifier); break; case GUIModChangeType.Install: toInstall.Add(change.Mod.Identifier); break; } } // Now work on satisifying dependencies. var recommended = new Dictionary <string, List <string> >(); var suggested = new Dictionary <string, List <string> >(); foreach (var change in opts.Key) { if (change.ChangeType == GUIModChangeType.Install) { AddMod(change.Mod.ToModule().recommends, recommended, change.Mod.Identifier, registry); AddMod(change.Mod.ToModule().suggests, suggested, change.Mod.Identifier, registry); } } ShowSelection(recommended); ShowSelection(suggested, true); tabController.HideTab("ChooseRecommendedModsTabPage"); if (installCanceled) { tabController.HideTab("WaitTabPage"); tabController.ShowTab("ManageModsTabPage"); e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key); return; } // Now let's make all our changes. tabController.RenameTab("WaitTabPage", "Status log"); tabController.ShowTab("WaitTabPage"); tabController.SetTabLock(true); var downloader = new NetAsyncModulesDownloader(GUI.user); cancelCallback = () => { downloader.CancelDownload(); installCanceled = true; }; GUI.user.RaiseMessage("About to install...\r\n"); // FIXME: here we should heat up the cache with any mods we're going to install, because // when this transaction types out it f***s up everything else. var resolvedMods = installer.ResolveModules(toInstall, opts.Value); foreach (var module in resolvedMods.CachedModules) { GUI.user.RaiseMessage(" * {0} {1}(cached)", module.name, module.version); } foreach (var module in resolvedMods.UncachedModules) { GUI.user.RaiseMessage(" * {0} {1}", module.name, module.version); } if (!GUI.user.RaiseYesNoDialog("\r\nContinue?")) { throw new CancelledActionKraken("User declined install list"); } GUI.user.RaiseMessage(String.Empty); // Just to look tidy. installer.EnsureCache(resolvedMods.UncachedModules); //Transaction is needed here to revert changes when an installation is cancelled //TODO: Cancellation should be handelt in the ModuleInstaller using (var transaction = CkanTransaction.CreateTransactionScope()) { //Set the result to false/failed in case we return e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key); SetDescription("Uninstalling selected mods"); if (!WasSuccessful(() => installer.UninstallList(toUninstall))) { return; } if (installCanceled) { return; } SetDescription("Updating selected mods"); if (!WasSuccessful(() => installer.Upgrade(toUpgrade, downloader))) { return; } if (installCanceled) { return; } // TODO: We should be able to resolve all our provisioning conflicts // before we start installing anything. CKAN.SanityChecker can be used to // pre-check if our changes are going to be consistent. bool resolvedAllProvidedMods = false; while (!resolvedAllProvidedMods) { if (installCanceled) { e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key); return; } var ret = InstallList(resolvedMods, opts.Value); if (!ret) { // install failed for some reason, error message is already displayed to the user e.Result = new KeyValuePair <bool, ModChanges>(false, opts.Key); return; } resolvedAllProvidedMods = true; } if (!installCanceled) { transaction.Complete(); } } e.Result = new KeyValuePair <bool, ModChanges>(!installCanceled, opts.Key); }