diff --git a/src/main/java/cz/kamma/kfmanager/service/FileOperations.java b/src/main/java/cz/kamma/kfmanager/service/FileOperations.java index e578bbb..fa35ca9 100644 --- a/src/main/java/cz/kamma/kfmanager/service/FileOperations.java +++ b/src/main/java/cz/kamma/kfmanager/service/FileOperations.java @@ -25,7 +25,8 @@ public class FileOperations { long totalSize = calculateTotalSize(items); final long[] currentCopied = {0}; - + final OverwriteResponse[] globalResponse = {null}; + for (FileItem item : items) { if (callback != null && callback.isCancelled()) break; File source = item.getFile(); @@ -34,10 +35,22 @@ public class FileOperations { // If target is the same as source (copying to the same directory), rename target if (source.getAbsolutePath().equals(target.getAbsolutePath())) { target = new File(targetDirectory, "copy-of-" + source.getName()); + } else if (target.exists()) { + if (globalResponse[0] == OverwriteResponse.NO_TO_ALL) continue; + if (globalResponse[0] != OverwriteResponse.YES_TO_ALL) { + OverwriteResponse res = callback.confirmOverwrite(target); + if (res == OverwriteResponse.CANCEL) break; + if (res == OverwriteResponse.NO_TO_ALL) { + globalResponse[0] = OverwriteResponse.NO_TO_ALL; + continue; + } + if (res == OverwriteResponse.NO) continue; + if (res == OverwriteResponse.YES_TO_ALL) globalResponse[0] = OverwriteResponse.YES_TO_ALL; + } } if (source.isDirectory()) { - copyDirectory(source.toPath(), target.toPath(), totalSize, currentCopied, callback); + copyDirectory(source.toPath(), target.toPath(), totalSize, currentCopied, callback, globalResponse); } else { copyFileWithProgress(source.toPath(), target.toPath(), totalSize, currentCopied, callback); } @@ -87,7 +100,7 @@ public class FileOperations { Files.setLastModifiedTime(target, Files.getLastModifiedTime(source)); } - private static void copyDirectory(Path source, Path target, long totalSize, final long[] totalCopied, ProgressCallback callback) throws IOException { + private static void copyDirectory(Path source, Path target, long totalSize, final long[] totalCopied, ProgressCallback callback, final OverwriteResponse[] globalResponse) throws IOException { Files.walkFileTree(source, new SimpleFileVisitor() { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { @@ -100,6 +113,21 @@ public class FileOperations { public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { if (callback != null && callback.isCancelled()) return FileVisitResult.TERMINATE; Path targetFile = target.resolve(source.relativize(file)); + + if (Files.exists(targetFile)) { + if (globalResponse[0] == OverwriteResponse.NO_TO_ALL) return FileVisitResult.CONTINUE; + if (globalResponse[0] != OverwriteResponse.YES_TO_ALL) { + OverwriteResponse res = callback.confirmOverwrite(targetFile.toFile()); + if (res == OverwriteResponse.CANCEL) return FileVisitResult.TERMINATE; + if (res == OverwriteResponse.NO_TO_ALL) { + globalResponse[0] = OverwriteResponse.NO_TO_ALL; + return FileVisitResult.CONTINUE; + } + if (res == OverwriteResponse.NO) return FileVisitResult.CONTINUE; + if (res == OverwriteResponse.YES_TO_ALL) globalResponse[0] = OverwriteResponse.YES_TO_ALL; + } + } + copyFileWithProgress(file, targetFile, totalSize, totalCopied, callback); return FileVisitResult.CONTINUE; } @@ -116,6 +144,7 @@ public class FileOperations { int current = 0; int total = items.size(); + final OverwriteResponse[] globalResponse = {null}; for (FileItem item : items) { if (callback != null && callback.isCancelled()) break; @@ -123,6 +152,20 @@ public class FileOperations { File source = item.getFile(); File target = new File(targetDirectory, source.getName()); + if (target.exists() && !source.getAbsolutePath().equals(target.getAbsolutePath())) { + if (globalResponse[0] == OverwriteResponse.NO_TO_ALL) continue; + if (globalResponse[0] != OverwriteResponse.YES_TO_ALL) { + OverwriteResponse res = callback.confirmOverwrite(target); + if (res == OverwriteResponse.CANCEL) break; + if (res == OverwriteResponse.NO_TO_ALL) { + globalResponse[0] = OverwriteResponse.NO_TO_ALL; + continue; + } + if (res == OverwriteResponse.NO) continue; + if (res == OverwriteResponse.YES_TO_ALL) globalResponse[0] = OverwriteResponse.YES_TO_ALL; + } + } + if (callback != null) { callback.onProgress(current, total, source.getName()); } @@ -374,9 +417,14 @@ public class FileOperations { /** * Callback for operation progress */ + public enum OverwriteResponse { + YES, NO, YES_TO_ALL, NO_TO_ALL, CANCEL + } + public interface ProgressCallback { void onProgress(long current, long total, String currentFile); default boolean isCancelled() { return false; } + default OverwriteResponse confirmOverwrite(File file) { return OverwriteResponse.YES; } } /** diff --git a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java index 2d3db90..4a73950 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java +++ b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java @@ -1566,33 +1566,59 @@ public class FilePanelTab extends JPanel { File targetDir = getCurrentDirectory(); Window parentWindow = SwingUtilities.getWindowAncestor(this); - String operationName = action == ClipboardService.ClipboardAction.CUT ? "Moving" : "Copying"; - ProgressDialog progressDialog = new ProgressDialog(parentWindow instanceof Frame ? (Frame)parentWindow : null, operationName); + String titleName = action == ClipboardService.ClipboardAction.CUT ? "Moving" : "Copying"; + ProgressDialog progressDialog = new ProgressDialog(parentWindow instanceof Frame ? (Frame)parentWindow : null, titleName); new Thread(() -> { try { + FileOperations.ProgressCallback callback = new FileOperations.ProgressCallback() { + @Override + public void onProgress(long current, long total, String currentFile) { + progressDialog.updateProgress(current, total, currentFile); + } + + @Override + public boolean isCancelled() { + return progressDialog.isCancelled(); + } + + @Override + public FileOperations.OverwriteResponse confirmOverwrite(File file) { + final FileOperations.OverwriteResponse[] result = new FileOperations.OverwriteResponse[1]; + try { + SwingUtilities.invokeAndWait(() -> { + Object[] options = {"Yes", "Yes to All", "No", "No to All", "Cancel"}; + int n = JOptionPane.showOptionDialog(progressDialog, + "File already exists: " + file.getName() + "\nOverwrite?", + "Overwrite Confirmation", + JOptionPane.DEFAULT_OPTION, + JOptionPane.QUESTION_MESSAGE, + null, + options, + options[0]); + + switch (n) { + case 0: result[0] = FileOperations.OverwriteResponse.YES; break; + case 1: result[0] = FileOperations.OverwriteResponse.YES_TO_ALL; break; + case 2: result[0] = FileOperations.OverwriteResponse.NO; break; + case 3: result[0] = FileOperations.OverwriteResponse.NO_TO_ALL; break; + default: + result[0] = FileOperations.OverwriteResponse.CANCEL; + progressDialog.cancel(); + break; + } + }); + } catch (Exception e) { + result[0] = FileOperations.OverwriteResponse.CANCEL; + } + return result[0]; + } + }; + if (action == ClipboardService.ClipboardAction.CUT) { - FileOperations.move(itemsToPaste, targetDir, new FileOperations.ProgressCallback() { - @Override - public void onProgress(long current, long total, String currentFile) { - progressDialog.updateProgress(current, total, currentFile); - } - @Override - public boolean isCancelled() { - return progressDialog.isCancelled(); - } - }); + FileOperations.move(itemsToPaste, targetDir, callback); } else { - FileOperations.copy(itemsToPaste, targetDir, new FileOperations.ProgressCallback() { - @Override - public void onProgress(long current, long total, String currentFile) { - progressDialog.updateProgress(current, total, currentFile); - } - @Override - public boolean isCancelled() { - return progressDialog.isCancelled(); - } - }); + FileOperations.copy(itemsToPaste, targetDir, callback); } SwingUtilities.invokeLater(() -> { progressDialog.dispose(); diff --git a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java index 84a2325..aaf405c 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java +++ b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java @@ -1781,6 +1781,38 @@ public class MainWindow extends JFrame { public boolean isCancelled() { return progressDialog.isCancelled(); } + + @Override + public FileOperations.OverwriteResponse confirmOverwrite(File file) { + final FileOperations.OverwriteResponse[] result = new FileOperations.OverwriteResponse[1]; + try { + SwingUtilities.invokeAndWait(() -> { + Object[] options = {"Yes", "Yes to All", "No", "No to All", "Cancel"}; + int n = JOptionPane.showOptionDialog(progressDialog, + "File already exists: " + file.getName() + "\nOverwrite?", + "Overwrite Confirmation", + JOptionPane.DEFAULT_OPTION, + JOptionPane.QUESTION_MESSAGE, + null, + options, + options[0]); + + switch (n) { + case 0: result[0] = FileOperations.OverwriteResponse.YES; break; + case 1: result[0] = FileOperations.OverwriteResponse.YES_TO_ALL; break; + case 2: result[0] = FileOperations.OverwriteResponse.NO; break; + case 3: result[0] = FileOperations.OverwriteResponse.NO_TO_ALL; break; + default: + result[0] = FileOperations.OverwriteResponse.CANCEL; + progressDialog.cancel(); + break; + } + }); + } catch (Exception e) { + result[0] = FileOperations.OverwriteResponse.CANCEL; + } + return result[0]; + } }; // Run operation in a background thread diff --git a/src/main/java/cz/kamma/kfmanager/ui/ProgressDialog.java b/src/main/java/cz/kamma/kfmanager/ui/ProgressDialog.java index dc055a9..d715fe7 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/ProgressDialog.java +++ b/src/main/java/cz/kamma/kfmanager/ui/ProgressDialog.java @@ -75,6 +75,10 @@ public class ProgressDialog extends JDialog { setLocationRelativeTo(owner); } + public void cancel() { + cancelled = true; + } + private boolean displayAsBytes = false; public void setDisplayAsBytes(boolean displayAsBytes) { diff --git a/src/main/resources/Copilot_20260117_114947.png b/src/main/resources/Copilot_20260117_114947.png new file mode 100644 index 0000000..97a8e13 Binary files /dev/null and b/src/main/resources/Copilot_20260117_114947.png differ diff --git a/src/main/resources/copy-of-Copilot_20260117_114947.png b/src/main/resources/copy-of-Copilot_20260117_114947.png new file mode 100644 index 0000000..97a8e13 Binary files /dev/null and b/src/main/resources/copy-of-Copilot_20260117_114947.png differ