From dcc9a6a418c26fcc3772e12d0bbb06482a1a46e2 Mon Sep 17 00:00:00 2001 From: rdavidek Date: Wed, 14 Jan 2026 20:35:24 +0100 Subject: [PATCH] added support for zip --- .../com/kfmanager/service/FileOperations.java | 95 ++++++++++++++++ .../java/com/kfmanager/ui/MainWindow.java | 107 ++++++++++++++++++ 2 files changed, 202 insertions(+) diff --git a/src/main/java/com/kfmanager/service/FileOperations.java b/src/main/java/com/kfmanager/service/FileOperations.java index f3a05e3..7a0a200 100644 --- a/src/main/java/com/kfmanager/service/FileOperations.java +++ b/src/main/java/com/kfmanager/service/FileOperations.java @@ -8,6 +8,7 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.zip.*; /** * Service for file operations - copy, move, delete, etc. @@ -237,6 +238,100 @@ public class FileOperations { } } + /** + * Zip files/directories into a target zip file + */ + public static void zip(List items, File targetZipFile, ProgressCallback callback) throws IOException { + try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(targetZipFile))) { + int current = 0; + int total = items.size(); + + for (FileItem item : items) { + current++; + File source = item.getFile(); + if (callback != null) { + callback.onProgress(current, total, source.getName()); + } + addToZip(source, source.getName(), zos); + } + } + } + + private static void addToZip(File fileToZip, String fileName, ZipOutputStream zos) throws IOException { + if (fileToZip.isHidden()) { + return; + } + if (fileToZip.isDirectory()) { + if (fileName.endsWith("/")) { + zos.putNextEntry(new ZipEntry(fileName)); + zos.closeEntry(); + } else { + zos.putNextEntry(new ZipEntry(fileName + "/")); + zos.closeEntry(); + } + File[] children = fileToZip.listFiles(); + if (children != null) { + for (File childFile : children) { + addToZip(childFile, fileName + "/" + childFile.getName(), zos); + } + } + return; + } + try (FileInputStream fis = new FileInputStream(fileToZip)) { + ZipEntry zipEntry = new ZipEntry(fileName); + zos.putNextEntry(zipEntry); + byte[] bytes = new byte[1024]; + int length; + while ((length = fis.read(bytes)) >= 0) { + zos.write(bytes, 0, length); + } + zos.closeEntry(); + } + } + + /** + * Unzip a zip file into a target directory + */ + public static void unzip(File zipFile, File targetDirectory, ProgressCallback callback) throws IOException { + if (!targetDirectory.exists()) { + Files.createDirectories(targetDirectory.toPath()); + } + + try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) { + ZipEntry entry; + // First pass or estimated count could be done, but keep it simple for now + while ((entry = zis.getNextEntry()) != null) { + File newFile = new File(targetDirectory, entry.getName()); + + if (callback != null) { + callback.onProgress(0, 0, entry.getName()); + } + + if (entry.isDirectory()) { + if (!newFile.isDirectory() && !newFile.mkdirs()) { + throw new IOException("Failed to create directory " + newFile); + } + } else { + // create parent directories if they don't exist + File parent = newFile.getParentFile(); + if (!parent.isDirectory() && !parent.mkdirs()) { + throw new IOException("Failed to create directory " + parent); + } + + // write file content + try (FileOutputStream fos = new FileOutputStream(newFile)) { + byte[] buffer = new byte[1024]; + int len; + while ((len = zis.read(buffer)) > 0) { + fos.write(buffer, 0, len); + } + } + } + zis.closeEntry(); + } + } + } + // legacy matchesPattern removed — filename wildcard handling is done via a precompiled Pattern /** diff --git a/src/main/java/com/kfmanager/ui/MainWindow.java b/src/main/java/com/kfmanager/ui/MainWindow.java index 1364b38..5fb0dd3 100644 --- a/src/main/java/com/kfmanager/ui/MainWindow.java +++ b/src/main/java/com/kfmanager/ui/MainWindow.java @@ -423,6 +423,16 @@ public class MainWindow extends JFrame { KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); + // Alt+F5 - Zip + rootPane.registerKeyboardAction(e -> zipFiles(), + KeyStroke.getKeyStroke(KeyEvent.VK_F5, InputEvent.ALT_DOWN_MASK), + JComponent.WHEN_IN_FOCUSED_WINDOW); + + // Alt+F9 - Unzip + rootPane.registerKeyboardAction(e -> unzipFiles(), + KeyStroke.getKeyStroke(KeyEvent.VK_F9, InputEvent.ALT_DOWN_MASK), + JComponent.WHEN_IN_FOCUSED_WINDOW); + // F6 - Move rootPane.registerKeyboardAction(e -> moveFiles(), KeyStroke.getKeyStroke(KeyEvent.VK_F6, 0), @@ -701,6 +711,101 @@ public class MainWindow extends JFrame { } } + /** + * Zip selected files + */ + private void zipFiles() { + List selectedItems = activePanel.getSelectedItems(); + if (selectedItems.isEmpty()) { + JOptionPane.showMessageDialog(this, + "No files selected", + "Zip", + JOptionPane.INFORMATION_MESSAGE); + return; + } + + String defaultName; + if (selectedItems.size() == 1) { + defaultName = selectedItems.get(0).getName(); + } else { + defaultName = activePanel.getCurrentDirectory().getName(); + if (defaultName == null || defaultName.isEmpty() || defaultName.equals("/") || defaultName.endsWith(":")) { + defaultName = "archive"; + } + } + + if (defaultName.contains(".")) { + int lastDot = defaultName.lastIndexOf('.'); + if (lastDot > 0) { + defaultName = defaultName.substring(0, lastDot); + } + } + defaultName += ".zip"; + + String zipName = JOptionPane.showInputDialog(this, "Enter zip filename:", defaultName); + if (zipName == null || zipName.trim().isEmpty()) { + return; + } + + if (!zipName.toLowerCase().endsWith(".zip")) { + zipName += ".zip"; + } + + FilePanel targetPanel = (activePanel == leftPanel) ? rightPanel : leftPanel; + File targetDir = targetPanel.getCurrentDirectory(); + File targetZip = new File(targetDir, zipName); + + if (targetZip.exists()) { + int confirm = JOptionPane.showConfirmDialog(this, + "File already exists. Overwrite?", "Zip", JOptionPane.YES_NO_OPTION); + if (confirm != JOptionPane.YES_OPTION) { + return; + } + } + + final File finalTargetZip = targetZip; + performFileOperation(() -> { + FileOperations.zip(selectedItems, finalTargetZip, null); + }, "Zabaleno do " + zipName, targetPanel); + } + + /** + * Unzip selected zip file + */ + private void unzipFiles() { + List selectedItems = activePanel.getSelectedItems(); + if (selectedItems.isEmpty()) { + JOptionPane.showMessageDialog(this, + "No files selected", + "Unzip", + JOptionPane.INFORMATION_MESSAGE); + return; + } + + File zipFile = selectedItems.get(0).getFile(); + if (!zipFile.getName().toLowerCase().endsWith(".zip")) { + JOptionPane.showMessageDialog(this, + "Selected file is not a ZIP archive", + "Unzip", + JOptionPane.ERROR_MESSAGE); + return; + } + + FilePanel targetPanel = (activePanel == leftPanel) ? rightPanel : leftPanel; + File targetDir = targetPanel.getCurrentDirectory(); + + int result = JOptionPane.showConfirmDialog(this, + String.format("Unzip %s to:\n%s", zipFile.getName(), targetDir.getAbsolutePath()), + "Unzip", + JOptionPane.OK_CANCEL_OPTION); + + if (result == JOptionPane.OK_OPTION) { + performFileOperation(() -> { + FileOperations.unzip(zipFile, targetDir, null); + }, "Rozbaleno do " + targetDir.getName(), targetPanel); + } + } + /** * Rename selected file */ @@ -914,6 +1019,8 @@ public class MainWindow extends JFrame { "Java 11\n\n" + "Keyboard shortcuts:\n" + "F5 - Copy\n" + + "Alt+F5 - Zip\n" + + "Alt+F9 - Unzip\n" + "F6 - Move\n" + "F7 - New directory\n" + "F8 - Delete\n" +