From 39aa6daa356f8679700c62dd39a0cba0925b6f73 Mon Sep 17 00:00:00 2001 From: Radek Davidek Date: Wed, 29 Apr 2026 19:35:17 +0200 Subject: [PATCH] auto update text files in archive after editing --- .../cz/kamma/kfmanager/ui/FileEditor.java | 10 +- .../cz/kamma/kfmanager/ui/MainWindow.java | 117 +++++++++++++++++- 2 files changed, 125 insertions(+), 2 deletions(-) diff --git a/src/main/java/cz/kamma/kfmanager/ui/FileEditor.java b/src/main/java/cz/kamma/kfmanager/ui/FileEditor.java index 97d5b38..06909d0 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/FileEditor.java +++ b/src/main/java/cz/kamma/kfmanager/ui/FileEditor.java @@ -127,6 +127,10 @@ public class FileEditor extends JFrame { this.onSaveSuccess = onSaveSuccess; } + public void setOnSaveSuccess(Runnable onSaveSuccess) { + this.onSaveSuccess = onSaveSuccess; + } + private void initSearchPanel() { searchPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 2)); searchPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.GRAY)); @@ -1286,12 +1290,16 @@ public class FileEditor extends JFrame { return; // message handled in SwingWorker } + if (onSaveSuccess != null) { + onSaveSuccess.run(); + } + JOptionPane.showMessageDialog(this, "File saved", "Success", JOptionPane.INFORMATION_MESSAGE); updateStatus(); - } catch (IOException e) { + } catch (Exception e) { JOptionPane.showMessageDialog(this, "Error during saving:\n" + e.getMessage(), "Error", diff --git a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java index 977f317..14e6a92 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java +++ b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java @@ -2514,7 +2514,7 @@ public class MainWindow extends JFrame { java.util.List cmd = new java.util.ArrayList<>(); cmd.add(ext); cmd.add(tempFile.getAbsolutePath()); - Process p = new ProcessBuilder(cmd).start(); + new ProcessBuilder(cmd).start(); // Optional: Monitor process to upload back on exit? // For now internal editor is safer for auto-upload. } catch (Exception ex) { @@ -2543,6 +2543,22 @@ public class MainWindow extends JFrame { } File file = item.getFile(); + FilePanelTab activeTab = activePanel != null ? activePanel.getCurrentTab() : null; + boolean fileInsideOpenedArchive = activeTab != null && activeTab.isDirectoryInsideOpenedArchive(file.getParentFile()); + boolean forceInternalEditor = fileInsideOpenedArchive && isLikelyTextFile(file); + + if (forceInternalEditor) { + FileEditor editor = new FileEditor(this, file, config, false); + editor.setOnSaveSuccess(() -> syncArchiveAfterInternalEdit(file)); + editor.addWindowListener(new java.awt.event.WindowAdapter() { + @Override + public void windowClosed(java.awt.event.WindowEvent e) { + requestFocusInActivePanel(); + } + }); + editor.setVisible(true); + return; + } // If an external editor is configured, try launching it with the file path String ext = config.getExternalEditorPath(); @@ -2572,6 +2588,105 @@ public class MainWindow extends JFrame { editor.setVisible(true); } + private boolean isLikelyTextFile(File file) { + if (file == null || !file.isFile()) { + return false; + } + int probeSize = 4096; + byte[] probe; + try { + long size = Files.size(file.toPath()); + int toRead = (int) Math.min(probeSize, size); + probe = new byte[toRead]; + try (java.io.InputStream in = Files.newInputStream(file.toPath())) { + int read = in.read(probe); + if (read < 0) { + return true; + } + if (read < toRead) { + byte[] trimmed = new byte[read]; + System.arraycopy(probe, 0, trimmed, 0, read); + probe = trimmed; + } + } + } catch (Exception e) { + return false; + } + + if (probe.length == 0) { + return true; + } + + int nonPrintable = 0; + for (byte b : probe) { + int value = b & 0xFF; + if (value == 0) { + return false; + } + boolean printable = value == 9 || value == 10 || value == 13 || (value >= 32 && value <= 126); + if (!printable) { + nonPrintable++; + } + } + return nonPrintable <= Math.max(1, probe.length / 6); + } + + private void syncArchiveAfterInternalEdit(File editedFile) { + try { + if (activePanel == null) { + return; + } + FilePanelTab activeTab = activePanel.getCurrentTab(); + if (activeTab == null) { + return; + } + File parentDir = editedFile != null ? editedFile.getParentFile() : null; + if (parentDir == null || !activeTab.isDirectoryInsideOpenedArchive(parentDir)) { + return; + } + if (!activeTab.canSyncOpenedArchive()) { + throw new IOException("Updating this archive format is not supported"); + } + + activeTab.syncOpenedArchiveChanges(new FileOperations.ProgressCallback() { + @Override + public void onProgress(long current, long total, String fileName) {} + + @Override + public void onFileProgress(long current, long total) {} + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public FileOperations.OverwriteResponse confirmOverwrite(File source, File target) { + return FileOperations.OverwriteResponse.YES; + } + + @Override + public FileOperations.ErrorResponse onError(File file, Exception e) { + return FileOperations.ErrorResponse.ABORT; + } + + @Override + public FileOperations.SymlinkResponse confirmSymlink(File file) { + return FileOperations.SymlinkResponse.FOLLOW; + } + + @Override + public String requestPassword(String archiveName) { + return null; + } + }); + + activePanel.refresh(false); + } catch (Exception ex) { + throw new RuntimeException("File saved, but archive sync failed: " + ex.getMessage(), ex); + } + } + /** * Compare files from both panels */