Add new rename file conflict action

This commit is contained in:
crschnick 2024-10-09 12:15:27 +00:00
parent 7f334c21e9
commit b35b63708d
15 changed files with 98 additions and 21 deletions

View file

@ -30,15 +30,22 @@ public class BrowserAlerts {
new ButtonType(AppI18n.get("replaceAll"), ButtonBar.ButtonData.OTHER),
FileConflictChoice.REPLACE_ALL);
}
map.put(new ButtonType(AppI18n.get("rename"), ButtonBar.ButtonData.OTHER), FileConflictChoice.RENAME);
if (multiple) {
map.put(
new ButtonType(AppI18n.get("renameAll"), ButtonBar.ButtonData.OTHER),
FileConflictChoice.RENAME_ALL);
}
return AppWindowHelper.showBlockingAlert(alert -> {
alert.setTitle(AppI18n.get("fileConflictAlertTitle"));
alert.setHeaderText(AppI18n.get("fileConflictAlertHeader"));
AppWindowHelper.setContent(
alert,
AppI18n.get(
multiple ? "fileConflictAlertContentMultiple" : "fileConflictAlertContent", file));
alert.setAlertType(Alert.AlertType.CONFIRMATION);
alert.getButtonTypes().clear();
alert.getDialogPane().setContent(AppWindowHelper.alertContentText(AppI18n.get(
multiple ? "fileConflictAlertContentMultiple" : "fileConflictAlertContent", file), 655));
alert.getDialogPane().setMinWidth(705);
alert.getDialogPane().setPrefWidth(705);
alert.getDialogPane().setMaxWidth(705);
map.sequencedKeySet()
.forEach(buttonType -> alert.getButtonTypes().add(buttonType));
})
@ -97,6 +104,8 @@ public class BrowserAlerts {
SKIP,
SKIP_ALL,
REPLACE,
REPLACE_ALL
REPLACE_ALL,
RENAME,
RENAME_ALL
}
}

View file

@ -12,6 +12,8 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class BrowserFileTransferOperation {
@ -63,41 +65,52 @@ public class BrowserFileTransferOperation {
this.progress.accept(progress);
}
private boolean handleChoice(FileSystem fileSystem, String target, boolean multiple) throws Exception {
private BrowserAlerts.FileConflictChoice handleChoice(FileSystem fileSystem, String target, boolean multiple) throws Exception {
if (lastConflictChoice == BrowserAlerts.FileConflictChoice.CANCEL) {
return false;
return BrowserAlerts.FileConflictChoice.CANCEL;
}
if (lastConflictChoice == BrowserAlerts.FileConflictChoice.REPLACE_ALL) {
return true;
return BrowserAlerts.FileConflictChoice.REPLACE;
}
if (lastConflictChoice == BrowserAlerts.FileConflictChoice.RENAME_ALL) {
return BrowserAlerts.FileConflictChoice.RENAME;
}
if (fileSystem.fileExists(target)) {
if (lastConflictChoice == BrowserAlerts.FileConflictChoice.SKIP_ALL) {
return false;
return BrowserAlerts.FileConflictChoice.SKIP;
}
var choice = BrowserAlerts.showFileConflictAlert(target, multiple);
if (choice == BrowserAlerts.FileConflictChoice.CANCEL) {
lastConflictChoice = BrowserAlerts.FileConflictChoice.CANCEL;
return false;
return BrowserAlerts.FileConflictChoice.CANCEL;
}
if (choice == BrowserAlerts.FileConflictChoice.SKIP) {
return false;
return BrowserAlerts.FileConflictChoice.SKIP;
}
if (choice == BrowserAlerts.FileConflictChoice.SKIP_ALL) {
lastConflictChoice = BrowserAlerts.FileConflictChoice.SKIP_ALL;
return false;
return BrowserAlerts.FileConflictChoice.SKIP;
}
if (choice == BrowserAlerts.FileConflictChoice.REPLACE_ALL) {
lastConflictChoice = BrowserAlerts.FileConflictChoice.REPLACE_ALL;
return true;
return BrowserAlerts.FileConflictChoice.REPLACE;
}
if (choice == BrowserAlerts.FileConflictChoice.RENAME_ALL) {
lastConflictChoice = BrowserAlerts.FileConflictChoice.RENAME_ALL;
return BrowserAlerts.FileConflictChoice.RENAME;
}
return choice;
}
return true;
return BrowserAlerts.FileConflictChoice.REPLACE;
}
public void execute() throws Exception {
@ -152,8 +165,15 @@ public class BrowserFileTransferOperation {
new IllegalArgumentException("Target directory " + targetFile + " does already exist"));
}
if (checkConflicts && !handleChoice(target.getFileSystem(), targetFile, files.size() > 1)) {
return;
if (checkConflicts) {
var fileConflictChoice = handleChoice(target.getFileSystem(), targetFile, files.size() > 1);
if (fileConflictChoice == BrowserAlerts.FileConflictChoice.SKIP || fileConflictChoice == BrowserAlerts.FileConflictChoice.CANCEL) {
return;
}
if (fileConflictChoice == BrowserAlerts.FileConflictChoice.RENAME) {
targetFile = renameFileLoop(target.getFileSystem(), targetFile, source.getKind() == FileKind.DIRECTORY);
}
}
var doesMove = transferMode == BrowserFileTransferMode.MOVE || transferMode == BrowserFileTransferMode.NORMAL;
@ -164,6 +184,33 @@ public class BrowserFileTransferOperation {
}
}
private String renameFileLoop(FileSystem fileSystem, String target, boolean dir) throws Exception {
// Who has more than 10 copies?
for (int i = 0; i < 10; i++) {
target = renameFile(target);
if ((dir && !fileSystem.directoryExists(target)) || (!dir && !fileSystem.fileExists(target))) {
return target;
}
}
return target;
}
private String renameFile(String target) {
var targetFile = new FilePath(target);
var name = targetFile.getFileName();
var pattern = Pattern.compile("(.+?) \\((\\d+)\\)\\.(.+)");
var matcher = pattern.matcher(name);
if (matcher.matches()) {
try {
var number = Integer.parseInt(matcher.group(2));
var newFile = targetFile.getParent().join(matcher.group(1) + " (" + (number + 1) + ")." + matcher.group(3));
return newFile.toString();
} catch (NumberFormatException e) {}
}
return targetFile.getBaseName() + " (" + 1 + ")." + targetFile.getExtension();
}
private void handleSingleAcrossFileSystems(FileEntry source) throws Exception {
if (target.getKind() != FileKind.DIRECTORY) {
throw new IllegalStateException("Target " + target.getPath() + " is not a directory");
@ -225,10 +272,15 @@ public class BrowserFileTransferOperation {
if (sourceFile.getKind() == FileKind.DIRECTORY) {
target.getFileSystem().mkdirs(targetFile);
} else if (sourceFile.getKind() == FileKind.FILE) {
if (checkConflicts
&& !handleChoice(
target.getFileSystem(), targetFile, files.size() > 1 || flatFiles.size() > 1)) {
continue;
if (checkConflicts) {
var fileConflictChoice = handleChoice(target.getFileSystem(), targetFile, files.size() > 1 || flatFiles.size() > 1);
if (fileConflictChoice == BrowserAlerts.FileConflictChoice.SKIP || fileConflictChoice == BrowserAlerts.FileConflictChoice.CANCEL) {
continue;
}
if (fileConflictChoice == BrowserAlerts.FileConflictChoice.RENAME) {
targetFile = renameFileLoop(target.getFileSystem(), targetFile, false);
}
}
transfer(sourceFile, targetFile, transferred, totalSize, start);

View file

@ -41,8 +41,12 @@ import java.util.function.Supplier;
public class AppWindowHelper {
public static Node alertContentText(String s) {
return alertContentText(s, 450);
}
public static Node alertContentText(String s, int width) {
var text = new Text(s);
text.setWrappingWidth(450);
text.setWrappingWidth(width);
AppFont.medium(text);
var sp = new StackPane(text);
sp.setPadding(new Insets(5));

View file

@ -530,3 +530,4 @@ dontAllowTerminalRestart=Tillad ikke genstart af terminal
dontAllowTerminalRestartDescription=Som standard kan terminalsessioner genstartes, når de er afsluttet inde fra terminalen. For at tillade dette accepterer XPipe disse eksterne anmodninger fra terminalen om at starte sessionen igen\n\nXPipe har ingen kontrol over terminalen, og hvor dette opkald kommer fra, så ondsindede lokale programmer kan også bruge denne funktion til at starte forbindelser gennem XPipe. Ved at deaktivere denne funktion forhindres dette scenarie.
openDocumentation=Åben dokumentation
openDocumentationDescription=Besøg XPipes dokumentationsside for dette problem
renameAll=Omdøb alle

View file

@ -524,3 +524,4 @@ dontAllowTerminalRestart=Terminal-Neustart nicht zulassen
dontAllowTerminalRestartDescription=Standardmäßig können Terminalsitzungen neu gestartet werden, nachdem sie vom Terminal aus beendet wurden. Um dies zu ermöglichen, akzeptiert XPipe diese externen Anfragen vom Terminal, um die Sitzung erneut zu starten\n\nXPipe hat keine Kontrolle über das Terminal und darüber, woher dieser Aufruf kommt. Daher können böswillige lokale Anwendungen diese Funktion ebenfalls nutzen, um Verbindungen über XPipe zu starten. Die Deaktivierung dieser Funktion verhindert dieses Szenario.
openDocumentation=Offene Dokumentation
openDocumentationDescription=Besuche die XPipe-Dokumentationsseite zu diesem Thema
renameAll=Alle umbenennen

View file

@ -530,3 +530,4 @@ dontAllowTerminalRestart=Don't allow terminal restart
dontAllowTerminalRestartDescription=By default, terminal sessions can be restarted after they ended from within the terminal. To allow this, XPipe will accept these external requests from the terminal to launch the session again\n\nXPipe doesn't have any control over the terminal and where this call comes from, so malicious local applications can use this functionality as well to launch connections through XPipe. Disabling this functionality prevents this scenario.
openDocumentation=Open documentation
openDocumentationDescription=Visit the XPipe docs page for this issue
renameAll=Rename all

View file

@ -511,3 +511,4 @@ dontAllowTerminalRestart=No permitir el reinicio del terminal
dontAllowTerminalRestartDescription=Por defecto, las sesiones de terminal pueden reiniciarse una vez finalizadas desde dentro del terminal. Para permitirlo, XPipe aceptará estas peticiones externas del terminal para iniciar de nuevo la sesión\n\nXPipe no tiene ningún control sobre el terminal y de dónde procede esta llamada, por lo que las aplicaciones locales maliciosas también pueden utilizar esta funcionalidad para lanzar conexiones a través de XPipe. Deshabilitar esta funcionalidad evita este escenario.
openDocumentation=Documentación abierta
openDocumentationDescription=Visita la página de documentación de XPipe sobre este tema
renameAll=Renombrar todo

View file

@ -511,3 +511,4 @@ dontAllowTerminalRestart=Ne pas autoriser le redémarrage du terminal
dontAllowTerminalRestartDescription=Par défaut, les sessions de terminal peuvent être relancées après s'être terminées depuis le terminal. Pour permettre cela, XPipe acceptera ces demandes externes du terminal pour relancer la session\n\nXPipe n'a aucun contrôle sur le terminal et sur la provenance de cet appel, de sorte que des applications locales malveillantes peuvent également utiliser cette fonctionnalité pour lancer des connexions par l'intermédiaire de XPipe. La désactivation de cette fonctionnalité permet d'éviter ce scénario.
openDocumentation=Documentation ouverte
openDocumentationDescription=Visite la page de documentation de XPipe pour ce problème
renameAll=Renommer tout

View file

@ -511,3 +511,4 @@ dontAllowTerminalRestart=Non consentire il riavvio del terminale
dontAllowTerminalRestartDescription=Per impostazione predefinita, le sessioni del terminale possono essere riavviate dopo la loro conclusione dall'interno del terminale stesso. Per consentire ciò, XPipe accetterà le seguenti richieste esterne dal terminale per avviare nuovamente la sessione\n\nXPipe non ha alcun controllo sul terminale e sulla provenienza di questa chiamata, quindi anche le applicazioni locali malintenzionate possono utilizzare questa funzionalità per avviare connessioni attraverso XPipe. Disabilitando questa funzionalità si evita questo scenario.
openDocumentation=Documentazione aperta
openDocumentationDescription=Visita la pagina dei documenti di XPipe per questo problema
renameAll=Rinomina tutti

View file

@ -511,3 +511,4 @@ dontAllowTerminalRestart=端末の再起動を許可しない
dontAllowTerminalRestartDescription=デフォルトでは、ターミナル・セッションはターミナル内から終了後に再開することができる。これを可能にするため、XPipeはターミナルからセッションを再び起動するための以下の外部リクエストを受け付ける。\n\nXPipeはターミナルとこの呼び出しの発信元を制御できないため、悪意のあるローカルアプリケーションはこの機能を使用してXPipe経由で接続を開始することができる。この機能を無効にすることで、このシナリオを防ぐことができる。
openDocumentation=ドキュメントを開く
openDocumentationDescription=この問題のXPipeドキュメントページを見る
renameAll=すべての名前を変更する

View file

@ -511,3 +511,4 @@ dontAllowTerminalRestart=Terminal opnieuw opstarten niet toestaan
dontAllowTerminalRestartDescription=Standaard kunnen terminalsessies opnieuw worden gestart nadat ze vanuit de terminal zijn beëindigd. Om dit mogelijk te maken, accepteert XPipe deze externe verzoeken van de terminal om de sessie opnieuw te starten\n\nXPipe heeft geen controle over de terminal en waar deze oproep vandaan komt, dus kwaadwillende lokale applicaties kunnen deze functionaliteit ook gebruiken om verbindingen via XPipe te starten. Het uitschakelen van deze functionaliteit voorkomt dit scenario.
openDocumentation=Open documentatie
openDocumentationDescription=Bezoek de XPipe docs pagina voor dit probleem
renameAll=Hernoem alle

View file

@ -511,3 +511,4 @@ dontAllowTerminalRestart=Não permitir o reinício do terminal
dontAllowTerminalRestartDescription=Por defeito, as sessões de terminal podem ser reiniciadas depois de terminarem a partir do terminal. Para permitir isso, o XPipe aceitará essas solicitações externas do terminal para iniciar a sessão novamente\n\nO XPipe não tem qualquer controlo sobre o terminal e sobre a origem desta chamada, pelo que as aplicações locais maliciosas também podem utilizar esta funcionalidade para iniciar ligações através do XPipe. Desativar esta funcionalidade evita este cenário.
openDocumentation=Abre a documentação
openDocumentationDescription=Visita a página de documentação do XPipe para esta questão
renameAll=Renomeia tudo

View file

@ -511,3 +511,4 @@ dontAllowTerminalRestart=Не разрешайте перезагрузку те
dontAllowTerminalRestartDescription=По умолчанию терминальные сессии могут быть перезапущены после их завершения изнутри терминала. Чтобы разрешить это, XPipe будет принимать такие внешние запросы от терминала, чтобы снова запустить сессию\n\nXPipe не имеет никакого контроля над терминалом и тем, откуда поступает этот вызов, поэтому вредоносные локальные приложения могут использовать эту функциональность и для запуска соединений через XPipe. Отключение этой функциональности предотвращает подобный сценарий.
openDocumentation=Открытая документация
openDocumentationDescription=Посетите страницу документации XPipe по этому вопросу
renameAll=Переименовать все

View file

@ -512,3 +512,4 @@ dontAllowTerminalRestart=Terminalin yeniden başlatılmasına izin verme
dontAllowTerminalRestartDescription=Varsayılan olarak, terminal oturumları terminal içinden sonlandırıldıktan sonra yeniden başlatılabilir. Buna izin vermek için XPipe, oturumu tekrar başlatmak üzere terminalden gelen şu harici istekleri kabul edecektir\n\nXPipe terminal ve bu çağrının nereden geldiği üzerinde herhangi bir kontrole sahip değildir, bu nedenle kötü niyetli yerel uygulamalar XPipe üzerinden bağlantı başlatmak için bu işlevi de kullanabilir. Bu işlevselliğin devre dışı bırakılması bu senaryoyu önler.
openDocumentation=ık dokümantasyon
openDocumentationDescription=Bu sorun için XPipe dokümanlar sayfasını ziyaret edin
renameAll=Tümünü yeniden adlandır

View file

@ -511,3 +511,4 @@ dontAllowTerminalRestart=不允许终端重启
dontAllowTerminalRestartDescription=默认情况下终端会话可以在终端内部结束后重新启动。为了做到这一点XPipe 将接受来自终端的这些外部请求,以再次启动会话\n\nXPipe无法控制终端以及该调用的来源因此恶意本地应用程序也可以使用该功能通过XPipe启动连接。禁用该功能可防止出现这种情况。
openDocumentation=开放文档
openDocumentationDescription=访问 XPipe 文档页面了解这一问题
renameAll=重命名所有