From aaf5ea7ce9cf165df7b1e3bccda085910e700155 Mon Sep 17 00:00:00 2001 From: Radek Davidek Date: Wed, 29 Apr 2026 19:55:22 +0200 Subject: [PATCH] better compare files --- .../kfmanager/ui/FileComparisonDialog.java | 124 +++++++++--------- .../cz/kamma/kfmanager/ui/MainWindow.java | 51 +++---- 2 files changed, 81 insertions(+), 94 deletions(-) diff --git a/src/main/java/cz/kamma/kfmanager/ui/FileComparisonDialog.java b/src/main/java/cz/kamma/kfmanager/ui/FileComparisonDialog.java index a8660f2..9b5b0a8 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/FileComparisonDialog.java +++ b/src/main/java/cz/kamma/kfmanager/ui/FileComparisonDialog.java @@ -14,9 +14,15 @@ import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.io.File; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Locale; @@ -83,6 +89,26 @@ public class FileComparisonDialog extends JFrame { dispose(); } }); + + // TC-like quick navigation between differences. + getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0), "nextDiff"); + getRootPane().getActionMap().put("nextDiff", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + jumpToDifference(1); + } + }); + getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_F3, KeyEvent.SHIFT_DOWN_MASK), "prevDiff"); + getRootPane().getActionMap().put("prevDiff", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + jumpToDifference(-1); + } + }); + + if (!differenceVisibleRows.isEmpty()) { + jumpToDifference(1); + } } private void initComponents() { @@ -104,8 +130,6 @@ public class FileComparisonDialog extends JFrame { JButton prevDiff = new JButton("Previous difference"); JButton nextDiff = new JButton("Next difference"); - JButton synchronizeButton = new JButton("Synchronize from selected rows"); - JButton clearSyncButton = new JButton("Clear sync"); JButton reloadButton = new JButton("Reload"); smartAlignCheck.addActionListener(e -> refreshComparison(false)); @@ -117,10 +141,7 @@ public class FileComparisonDialog extends JFrame { prevDiff.addActionListener(e -> jumpToDifference(-1)); nextDiff.addActionListener(e -> jumpToDifference(1)); - synchronizeButton.addActionListener(e -> synchronizeFromSelectedRows()); - clearSyncButton.addActionListener(e -> clearSynchronization()); - - panel.add(new JLabel("Options:")); + panel.add(new JLabel("Compare:")); panel.add(smartAlignCheck); panel.add(ignoreCaseCheck); panel.add(ignoreTrimCheck); @@ -128,8 +149,6 @@ public class FileComparisonDialog extends JFrame { panel.add(onlyDifferencesCheck); panel.add(prevDiff); panel.add(nextDiff); - panel.add(synchronizeButton); - panel.add(clearSyncButton); panel.add(reloadButton); return panel; } @@ -204,56 +223,6 @@ public class FileComparisonDialog extends JFrame { refreshComparison(true); } - private void synchronizeFromSelectedRows() { - if (selectedLeftVisibleRow < 0 || selectedRightVisibleRow < 0) { - JOptionPane.showMessageDialog(this, - "Select one line in left pane and one line in right pane first.", - "Synchronize", - JOptionPane.INFORMATION_MESSAGE); - return; - } - if (selectedLeftVisibleRow >= visibleIndices.size() || selectedRightVisibleRow >= visibleIndices.size()) { - return; - } - - AlignedLine leftLine = alignedLines.get(visibleIndices.get(selectedLeftVisibleRow)); - AlignedLine rightLine = alignedLines.get(visibleIndices.get(selectedRightVisibleRow)); - - if (leftLine.leftNumber <= 0 || rightLine.rightNumber <= 0) { - JOptionPane.showMessageDialog(this, - "Selected rows must contain real lines in both panes (not empty gap rows).", - "Synchronize", - JOptionPane.WARNING_MESSAGE); - return; - } - - manualAnchorLeftLine = leftLine.leftNumber; - manualAnchorRightLine = rightLine.rightNumber; - refreshComparison(false); - focusAnchorRow(); - } - - private void clearSynchronization() { - manualAnchorLeftLine = -1; - manualAnchorRightLine = -1; - refreshComparison(false); - } - - private void focusAnchorRow() { - if (manualAnchorLeftLine <= 0 || manualAnchorRightLine <= 0) return; - for (int visibleRow = 0; visibleRow < visibleIndices.size(); visibleRow++) { - AlignedLine line = alignedLines.get(visibleIndices.get(visibleRow)); - if (line.leftNumber == manualAnchorLeftLine && line.rightNumber == manualAnchorRightLine) { - selectedVisibleRow = visibleRow; - selectedLeftVisibleRow = visibleRow; - selectedRightVisibleRow = visibleRow; - render(); - updateStatus(); - return; - } - } - } - private void refreshComparison(boolean resetSelection) { ComparisonOptions options = getOptions(); List comparableLeft = buildComparableLines(sourceLines1, options); @@ -445,6 +414,13 @@ public class FileComparisonDialog extends JFrame { for (AlignedLine line : alignedLines) { if (line.different) different++; } + String diffPos; + if (differenceVisibleRows.isEmpty()) { + diffPos = "difference 0/0"; + } else { + int pointer = selectedDifferencePointer >= 0 ? selectedDifferencePointer + 1 : 1; + diffPos = "difference " + pointer + "/" + differenceVisibleRows.size(); + } String alignMode = smartAlignCheck.isSelected() ? "smart" : "by position"; String visibleInfo = onlyDifferencesCheck.isSelected() ? "showing only differences" @@ -452,7 +428,7 @@ public class FileComparisonDialog extends JFrame { String syncInfo = (manualAnchorLeftLine > 0 && manualAnchorRightLine > 0) ? " | sync L" + manualAnchorLeftLine + " -> R" + manualAnchorRightLine : ""; - statusLabel.setText("Differences: " + different + " | " + visibleInfo + " | align: " + alignMode + syncInfo); + statusLabel.setText("Differences: " + different + " | " + diffPos + " | " + visibleInfo + " | align: " + alignMode + syncInfo + " | F3/Shift+F3 next/prev"); } private void applyAppearance() { @@ -635,11 +611,31 @@ public class FileComparisonDialog extends JFrame { } private List readLines(File f) throws IOException { - try { - return Files.readAllLines(f.toPath(), StandardCharsets.UTF_8); - } catch (Exception e) { - return Files.readAllLines(f.toPath()); + byte[] bytes = Files.readAllBytes(f.toPath()); + if (bytes.length == 0) { + return List.of(); } + + // Total Commander-like behavior: tolerate legacy encodings instead of failing. + String[] encodings = {"UTF-8", "windows-1250", "ISO-8859-1"}; + String decoded = null; + for (String enc : encodings) { + try { + CharsetDecoder decoder = Charset.forName(enc).newDecoder(); + decoder.onMalformedInput(CodingErrorAction.REPORT); + decoder.onUnmappableCharacter(CodingErrorAction.REPORT); + decoded = decoder.decode(ByteBuffer.wrap(bytes)).toString(); + break; + } catch (CharacterCodingException ignored) { + // Try next encoding fallback. + } + } + + if (decoded == null) { + decoded = new String(bytes, StandardCharsets.ISO_8859_1); + } + + return Arrays.asList(decoded.split("\\R", -1)); } private static class ComparisonOptions { @@ -665,4 +661,4 @@ public class FileComparisonDialog extends JFrame { this.different = different; } } -} +} diff --git a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java index e11a48b..86aa368 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java +++ b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java @@ -3020,36 +3020,10 @@ public class MainWindow extends JFrame { List leftSelection = leftPanel.getSelectedItems(); List rightSelection = rightPanel.getSelectedItems(); - File leftFile = null; - File rightFile = null; - - if (leftSelection.size() == 1) { - leftFile = leftSelection.getFirst().getFile(); - } else if (leftSelection.size() > 1) { - JOptionPane.showMessageDialog(this, "Please select only one file in the left panel.", "Compare Files", JOptionPane.WARNING_MESSAGE); - return; - } - - if (rightSelection.size() == 1) { - rightFile = rightSelection.getFirst().getFile(); - } else if (rightSelection.size() > 1) { - JOptionPane.showMessageDialog(this, "Please select only one file in the right panel.", "Compare Files", JOptionPane.WARNING_MESSAGE); - return; - } - - // If nothing explicitly selected (marked) in a panel, use the focused item - if (leftFile == null) { - FileItem focused = leftPanel.getFocusedItem(); - if (focused != null && !focused.getName().equals("..")) { - leftFile = focused.getFile(); - } - } - if (rightFile == null) { - FileItem focused = rightPanel.getFocusedItem(); - if (focused != null && !focused.getName().equals("..")) { - rightFile = focused.getFile(); - } - } + // Total Commander-like behavior: compare focused file from each panel. + // If exactly one item is selected in a panel, use that one as an override. + File leftFile = getCompareCandidate(leftPanel, leftSelection); + File rightFile = getCompareCandidate(rightPanel, rightSelection); if (leftFile == null || rightFile == null) { JOptionPane.showMessageDialog(this, "Please select a file in both panels to compare.", "Compare Files", JOptionPane.WARNING_MESSAGE); @@ -3064,6 +3038,23 @@ public class MainWindow extends JFrame { FileComparisonDialog dlg = new FileComparisonDialog(this, leftFile, rightFile, config); dlg.setVisible(true); } + + private File getCompareCandidate(FilePanel panel, List selected) { + if (panel == null) return null; + + if (selected != null && selected.size() == 1) { + FileItem item = selected.getFirst(); + if (item != null && !"..".equals(item.getName())) { + return item.getFile(); + } + } + + FileItem focused = panel.getFocusedItem(); + if (focused != null && !"..".equals(focused.getName())) { + return focused.getFile(); + } + return null; + } /** * Refresh both panels while preserving selection and active panel focus.