search in file

This commit is contained in:
Radek Davidek 2026-01-20 09:50:16 +01:00
parent 68fe5ca0c6
commit 26d62d2902

View File

@ -35,11 +35,18 @@ public class FileEditor extends JDialog {
// Allow loading entire file into memory up to this limit (100 MB) // Allow loading entire file into memory up to this limit (100 MB)
private final long maxFullLoadBytes = 100L * 1024L * 1024L; // 100 MB private final long maxFullLoadBytes = 100L * 1024L * 1024L; // 100 MB
private JPanel hexControlPanel = null; private JPanel hexControlPanel = null;
private JPanel northPanel = null;
private JButton prevPageBtn = null; private JButton prevPageBtn = null;
private JButton nextPageBtn = null; private JButton nextPageBtn = null;
private JLabel pageOffsetLabel = null; private JLabel pageOffsetLabel = null;
private javax.swing.undo.UndoManager undoManager; private javax.swing.undo.UndoManager undoManager;
// Search support
private JPanel searchPanel;
private JTextField searchField;
private JLabel searchStatusLabel;
private static String lastSearchValue = "";
public FileEditor(Window parent, File file, AppConfig config, boolean readOnly) { public FileEditor(Window parent, File file, AppConfig config, boolean readOnly) {
super(parent, (readOnly ? "Prohlížeč - " : "Editor - ") + file.getName(), ModalityType.MODELESS); super(parent, (readOnly ? "Prohlížeč - " : "Editor - ") + file.getName(), ModalityType.MODELESS);
this.file = file; this.file = file;
@ -61,9 +68,164 @@ public class FileEditor extends JDialog {
}); });
} }
private void initSearchPanel() {
searchPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 2));
searchPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.GRAY));
searchField = new JTextField(20);
searchField.addActionListener(e -> findNext());
JButton nextBtn = new JButton("Dále");
nextBtn.addActionListener(e -> findNext());
JButton prevBtn = new JButton("Zpět");
prevBtn.addActionListener(e -> findPrevious());
JButton closeBtn = new JButton("X");
closeBtn.setMargin(new Insets(0, 5, 0, 5));
closeBtn.addActionListener(e -> hideSearchPanel());
searchStatusLabel = new JLabel("");
searchPanel.add(new JLabel("Hledat:"));
searchPanel.add(searchField);
searchPanel.add(nextBtn);
searchPanel.add(prevBtn);
searchPanel.add(closeBtn);
searchPanel.add(searchStatusLabel);
searchPanel.setVisible(false);
}
private void showSearchPanel() {
searchPanel.setVisible(true);
String selection = textArea.getSelectedText();
if (selection != null && !selection.isEmpty() && !selection.contains("\n")) {
searchField.setText(selection);
lastSearchValue = selection;
} else if (!lastSearchValue.isEmpty()) {
searchField.setText(lastSearchValue);
} else if (config != null) {
java.util.List<String> hist = config.getContentSearchHistory();
if (hist != null && !hist.isEmpty()) {
lastSearchValue = hist.get(0);
searchField.setText(lastSearchValue);
}
}
searchField.requestFocusInWindow();
searchField.selectAll();
SwingUtilities.invokeLater(() -> {
searchField.requestFocusInWindow();
searchField.selectAll();
});
if (northPanel != null) {
northPanel.revalidate();
northPanel.repaint();
}
revalidate();
}
private void hideSearchPanel() {
searchPanel.setVisible(false);
textArea.requestFocusInWindow();
revalidate();
}
private void findNext() {
String text = searchField.getText();
if (text.isEmpty()) text = lastSearchValue;
if (text == null || text.isEmpty()) return;
lastSearchValue = text;
updateSearchHistory(text);
if (searchField.getText().isEmpty()) searchField.setText(text);
String content = textArea.getText();
int start = textArea.getCaretPosition();
// If we have a selection that matches, start after it
if (textArea.getSelectionEnd() > textArea.getSelectionStart()) {
if (content.substring(textArea.getSelectionStart(), textArea.getSelectionEnd()).equalsIgnoreCase(text)) {
start = textArea.getSelectionEnd();
}
}
int idx = content.toLowerCase().indexOf(text.toLowerCase(), start);
if (idx == -1) {
// wrap around
idx = content.toLowerCase().indexOf(text.toLowerCase(), 0);
}
if (idx != -1) {
textArea.setSelectionStart(idx);
textArea.setSelectionEnd(idx + text.length());
textArea.getCaret().setSelectionVisible(true);
try {
Rectangle rect = textArea.modelToView2D(idx).getBounds();
textArea.scrollRectToVisible(rect);
} catch (Exception ignore) {}
searchStatusLabel.setText("");
} else {
searchStatusLabel.setText("Nenalezeno");
}
}
private void findPrevious() {
String text = searchField.getText();
if (text.isEmpty()) text = lastSearchValue;
if (text == null || text.isEmpty()) return;
lastSearchValue = text;
updateSearchHistory(text);
if (searchField.getText().isEmpty()) searchField.setText(text);
String content = textArea.getText();
int start = textArea.getSelectionStart() - 1;
if (start < 0) start = content.length() - 1;
int idx = content.toLowerCase().lastIndexOf(text.toLowerCase(), start);
if (idx == -1) {
// wrap around
idx = content.toLowerCase().lastIndexOf(text.toLowerCase(), content.length() - 1);
}
if (idx != -1) {
textArea.setSelectionStart(idx);
textArea.setSelectionEnd(idx + text.length());
textArea.getCaret().setSelectionVisible(true);
try {
Rectangle rect = textArea.modelToView2D(idx).getBounds();
textArea.scrollRectToVisible(rect);
} catch (Exception ignore) {}
searchStatusLabel.setText("");
} else {
searchStatusLabel.setText("Nenalezeno");
}
}
private void updateSearchHistory(String text) {
if (config == null || text == null || text.isEmpty()) return;
java.util.List<String> hist = new java.util.ArrayList<>(config.getContentSearchHistory());
hist.remove(text);
hist.add(0, text);
if (hist.size() > 20) hist = hist.subList(0, 20);
config.saveContentSearchHistory(hist);
config.saveConfig();
}
private void initComponents() { private void initComponents() {
setLayout(new BorderLayout()); setLayout(new BorderLayout());
northPanel = new JPanel();
northPanel.setLayout(new BoxLayout(northPanel, BoxLayout.Y_AXIS));
add(northPanel, BorderLayout.NORTH);
initSearchPanel();
northPanel.add(searchPanel);
// Menu bar // Menu bar
createMenuBar(); createMenuBar();
@ -284,6 +446,18 @@ public class FileEditor extends JDialog {
selectAllItem.addActionListener(e -> textArea.selectAll()); selectAllItem.addActionListener(e -> textArea.selectAll());
editMenu.add(selectAllItem); editMenu.add(selectAllItem);
editMenu.addSeparator();
JMenuItem findItem = new JMenuItem("Hledat...");
findItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK));
findItem.addActionListener(e -> showSearchPanel());
editMenu.add(findItem);
JMenuItem findNextItem = new JMenuItem("Hledat další");
findNextItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0));
findNextItem.addActionListener(e -> findNext());
editMenu.add(findNextItem);
// Update menu items state when menu is selected // Update menu items state when menu is selected
editMenu.addMenuListener(new javax.swing.event.MenuListener() { editMenu.addMenuListener(new javax.swing.event.MenuListener() {
@Override @Override
@ -352,8 +526,14 @@ public class FileEditor extends JDialog {
JComponent.WHEN_IN_FOCUSED_WINDOW); JComponent.WHEN_IN_FOCUSED_WINDOW);
} }
// ESC - Zavřít // ESC - Zavřít nebo skrýt hledání
rootPane.registerKeyboardAction(e -> closeEditor(), rootPane.registerKeyboardAction(e -> {
if (searchPanel.isVisible()) {
hideSearchPanel();
} else {
closeEditor();
}
},
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
JComponent.WHEN_IN_FOCUSED_WINDOW); JComponent.WHEN_IN_FOCUSED_WINDOW);
@ -394,6 +574,19 @@ public class FileEditor extends JDialog {
rootPane.registerKeyboardAction(e -> { rootPane.registerKeyboardAction(e -> {
if (undoManager.canRedo()) undoManager.redo(); if (undoManager.canRedo()) undoManager.redo();
}, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK), JComponent.WHEN_IN_FOCUSED_WINDOW); }, KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK), JComponent.WHEN_IN_FOCUSED_WINDOW);
// Hledání
rootPane.registerKeyboardAction(e -> showSearchPanel(),
KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK),
JComponent.WHEN_IN_FOCUSED_WINDOW);
rootPane.registerKeyboardAction(e -> findNext(),
KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0),
JComponent.WHEN_IN_FOCUSED_WINDOW);
rootPane.registerKeyboardAction(e -> findPrevious(),
KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK),
JComponent.WHEN_IN_FOCUSED_WINDOW);
} }
private void setHexMode(boolean on) { private void setHexMode(boolean on) {
@ -422,9 +615,10 @@ public class FileEditor extends JDialog {
textArea.setFont(new Font("Monospaced", Font.PLAIN, textArea.getFont().getSize())); textArea.setFont(new Font("Monospaced", Font.PLAIN, textArea.getFont().getSize()));
ensureHexControls(); ensureHexControls();
if (hexControlPanel.getParent() == null) { if (hexControlPanel.getParent() == null) {
add(hexControlPanel, BorderLayout.NORTH); northPanel.add(hexControlPanel);
} }
hexControlPanel.setVisible(true); hexControlPanel.setVisible(true);
northPanel.revalidate();
} else { } else {
// switch back to text view if possible // switch back to text view if possible
// close RA if open // close RA if open
@ -434,6 +628,10 @@ public class FileEditor extends JDialog {
loadFile(); loadFile();
textArea.setEditable(!readOnly); textArea.setEditable(!readOnly);
textArea.setFont(config.getEditorFont()); textArea.setFont(config.getEditorFont());
if (hexControlPanel != null) {
hexControlPanel.setVisible(false);
northPanel.revalidate();
}
} }
applyAppearance(); applyAppearance();
updateStatus(); updateStatus();
@ -593,8 +791,9 @@ public class FileEditor extends JDialog {
pageOffsetBytes = 0L; pageOffsetBytes = 0L;
loadHexPage(); loadHexPage();
ensureHexControls(); ensureHexControls();
if (hexControlPanel.getParent() == null) add(hexControlPanel, BorderLayout.NORTH); if (hexControlPanel.getParent() == null) northPanel.add(hexControlPanel);
hexControlPanel.setVisible(true); hexControlPanel.setVisible(true);
northPanel.revalidate();
textArea.setEditable(false); textArea.setEditable(false);
textArea.setFont(new Font("Monospaced", Font.PLAIN, textArea.getFont().getSize())); textArea.setFont(new Font("Monospaced", Font.PLAIN, textArea.getFont().getSize()));
hexMode = true; hexMode = true;
@ -608,8 +807,9 @@ public class FileEditor extends JDialog {
textArea.setEditable(false); textArea.setEditable(false);
textArea.setFont(new Font("Monospaced", Font.PLAIN, textArea.getFont().getSize())); textArea.setFont(new Font("Monospaced", Font.PLAIN, textArea.getFont().getSize()));
ensureHexControls(); ensureHexControls();
if (hexControlPanel.getParent() == null) add(hexControlPanel, BorderLayout.NORTH); if (hexControlPanel.getParent() == null) northPanel.add(hexControlPanel);
hexControlPanel.setVisible(true); hexControlPanel.setVisible(true);
northPanel.revalidate();
} else if (hexMode) { } else if (hexMode) {
buildHexViewText(0L); buildHexViewText(0L);
} else { } else {