better compare files
This commit is contained in:
parent
351b59065e
commit
aaf5ea7ce9
@ -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<String> 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<String> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3020,36 +3020,10 @@ public class MainWindow extends JFrame {
|
||||
List<FileItem> leftSelection = leftPanel.getSelectedItems();
|
||||
List<FileItem> 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<FileItem> 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.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user