internal static async Task <bool> Add(IWin32Window owner, List <string> droppedItemsList) { if (Config.FMArchivePaths.Count == 0) { return(false); } // Drag-and-drop operations block not only the app thread, but also the thread of the Explorer window // from which you dragged the files. Good lord. So shove the entire thing into another thread so our // drag-and-drop operation can finish in the UI thread and unblock Explorer. bool success = await Task.Run(() => { string archivesLines = ""; bool archivesLinesTruncated = false; const int maxArchivesLines = 15; for (int i = 0, archiveLineCount = 0; i < droppedItemsList.Count; i++) { string di = droppedItemsList[i]; if (di.IsEmpty() || !di.ExtIsArchive() || /* * Reject tomfoolery where a directory could be named "whatever.zip" etc. * Don't do this in the drag-over handler, because we don't want to potentially take a long * wait to hit the disk there. It does mean that for directories that are named like archive * files we'll have a "you can do this drop operation" icon and then do nothing once we get * here, but that should be a rare case anyway. */ Directory.Exists(di)) { droppedItemsList.RemoveAt(i); i--; continue; } else if (PathContainsUnsupportedProgramFilesFolder(di, out string progFilesPath)) { string message = "This path contains '" + progFilesPath + "' which is an unsupported path for 32-bit apps.\r\n\r\n" + "The passed path was:\r\n\r\n" + di + "\r\n\r\n"; Log(message, stackTrace: true); Core.View.InvokeSync(() => Dialogs.ShowError(message, owner)); return(false); } if (!archivesLinesTruncated) { if (!archivesLines.IsEmpty()) { archivesLines += "\r\n"; } if (archiveLineCount < maxArchivesLines) { archivesLines += di; archiveLineCount++; } else if (archiveLineCount == maxArchivesLines) { archivesLines += "[...]"; archivesLinesTruncated = true; } } } if (droppedItemsList.Count == 0) { return(false); } bool singleArchive = droppedItemsList.Count == 1; bool singleArchivePath = Config.FMArchivePaths.Count == 1; string destDir = ""; if (!singleArchivePath) { DialogResult result = (DialogResult)Core.View.InvokeSync(new Func <DialogResult>(() => { using var f = new MessageBoxCustomForm( messageTop: (singleArchive ? LText.AddFMsToSet.AddFM_Dialog_AskMessage : LText.AddFMsToSet.AddFMs_Dialog_AskMessage) + "\r\n\r\n" + archivesLines + "\r\n\r\n" + (singleArchive ? LText.AddFMsToSet.AddFM_Dialog_ChooseArchiveDir : LText.AddFMsToSet.AddFMs_Dialog_ChooseArchiveDir), messageBottom: "", title: singleArchive ? LText.AddFMsToSet.AddFM_DialogTitle : LText.AddFMsToSet.AddFMs_DialogTitle, icon: MessageBoxIcon.None, okText: LText.AddFMsToSet.AddFM_Add, cancelText: LText.Global.Cancel, okIsDangerous: false, choiceStrings: Config.FMArchivePaths.ToArray(), multiSelectionAllowed: false); // Show with explicit owner because otherwise we get in a "halfway" state where the dialog is // modal, but it can be made to be underneath the main window and then you can never get back // to it again and have to kill the app through Task Manager. DialogResult result = f.ShowDialogDark(owner); if (result != DialogResult.OK) { return(DialogResult.Cancel); } destDir = f.SelectedItems[0]; return(DialogResult.OK); })); if (result != DialogResult.OK) { return(false); } } else { destDir = Config.FMArchivePaths[0]; } int successfulFilesCopiedCount = 0; foreach (string file in droppedItemsList) { string destFile = Path.Combine(destDir, Path.GetFileName(file)); try { FileSystem.CopyFile(file, destFile, UIOption.AllDialogs, UICancelOption.DoNothing); successfulFilesCopiedCount++; } catch (Exception ex) { Log("Exception copying archive '" + file + "' to '" + destDir, ex); Core.View.InvokeSync(new Action(() => Dialogs.ShowError(LText.AlertMessages.AddFM_UnableToCopy + "\r\n\r\nSource FM archive file: " + file + "\r\n\r\nDestination directory: " + destDir))); } } return(successfulFilesCopiedCount > 0); }); if (!success) { return(false); } await Core.RefreshFMsListFromDisk(); return(true); }
/// <summary> /// Deletes <paramref name="fm"/>'s archive from disk, asking the user for confirmation first. /// </summary> /// <param name="fm"></param> /// <returns></returns> internal static async Task Delete(FanMission fm) { if (fm.MarkedUnavailable) { return; } var archives = FindAllMatches(fm.Archive); if (archives.Count == 0) { Dialogs.ShowAlert(LText.FMDeletion.ArchiveNotFound, LText.AlertMessages.DeleteFMArchive, MessageBoxIcon.Error); return; } bool singleArchive = archives.Count == 1; var finalArchives = new List <string>(); using (var f = new MessageBoxCustomForm( messageTop: singleArchive ? LText.FMDeletion.AboutToDelete + "\r\n\r\n" + archives[0] : LText.FMDeletion.DuplicateArchivesFound, messageBottom: "", title: LText.AlertMessages.DeleteFMArchive, icon: MessageBoxIcon.Warning, okText: singleArchive ? LText.FMDeletion.DeleteFM : LText.FMDeletion.DeleteFMs, cancelText: LText.Global.Cancel, okIsDangerous: true, choiceStrings: singleArchive ? null : archives.ToArray())) { if (f.ShowDialogDark() != DialogResult.OK) { return; } finalArchives.AddRange(singleArchive ? archives : f.SelectedItems); } if (fm.Installed) { (bool cancel, bool cont, _) = Dialogs.AskToContinueWithCancelCustomStrings( message: LText.FMDeletion.AskToUninstallFMFirst, title: LText.AlertMessages.DeleteFMArchive, icon: MessageBoxIcon.Warning, showDontAskAgain: false, yes: LText.AlertMessages.Uninstall, no: LText.AlertMessages.LeaveInstalled, cancel: LText.Global.Cancel ); if (cancel) { return; } if (cont) { bool canceled = !await FMInstallAndPlay.UninstallFM(fm); if (canceled) { return; } } } try { Core.View.ShowProgressBox(ProgressTask.DeleteFMArchive); await Task.Run(() => { foreach (string archive in finalArchives) { try { FileSystem.DeleteFile(archive, UIOption.AllDialogs, RecycleOption.SendToRecycleBin); } catch (Exception ex) { Log("Exception deleting archive '" + archive + "'", ex); Core.View.InvokeSync(new Action(() => Dialogs.ShowError(LText.AlertMessages.DeleteFM_UnableToDelete + "\r\n\r\n" + archive))); } } }); } finally { var newArchives = await Task.Run(() => FindAllMatches(fm.Archive)); Core.View.HideProgressBox(); if (newArchives.Count == 0 && !fm.Installed) { fm.MarkedUnavailable = true; await Core.View.SortAndSetFilter(keepSelection : true); } } }