diff --git a/src/main/java/cz/kamma/kfmanager/ui/FilePanel.java b/src/main/java/cz/kamma/kfmanager/ui/FilePanel.java index 8471547..0a144e2 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/FilePanel.java +++ b/src/main/java/cz/kamma/kfmanager/ui/FilePanel.java @@ -34,6 +34,12 @@ public class FilePanel extends JPanel { if (tab != null) tab.startInlineRename(); } + /** Select files by wildcard pattern in the current tab. */ + public void selectByWildcard(String pattern) { + FilePanelTab tab = getCurrentTab(); + if (tab != null) tab.selectByWildcard(pattern); + } + private Runnable switchPanelCallback; public void setSwitchPanelCallback(Runnable cb) { diff --git a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java index bbc495a..16bfba6 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java +++ b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java @@ -1564,6 +1564,91 @@ public class FilePanelTab extends JPanel { updateStatus(); } + /** + * Select all items that match the given wildcard pattern. + * Supports * (any characters) and ? (single character). + * Example patterns: *.txt, test*.*, file?.dat + */ + public void selectByWildcard(String pattern) { + if (pattern == null || pattern.trim().isEmpty()) { + return; + } + + pattern = pattern.trim(); + + // Convert wildcard pattern to regex + String regex = wildcardToRegex(pattern); + java.util.regex.Pattern compiledPattern; + try { + compiledPattern = java.util.regex.Pattern.compile(regex, java.util.regex.Pattern.CASE_INSENSITIVE); + } catch (Exception e) { + return; // Invalid pattern + } + + // Mark all matching items + int matchCount = 0; + for (FileItem item : tableModel.items) { + if (item.getName().equals("..")) { + continue; // Skip parent directory + } + + if (compiledPattern.matcher(item.getName()).matches()) { + item.setMarked(true); + matchCount++; + } + } + + // Refresh the table to show the marked items + fileTable.repaint(); + updateStatus(); + + // Request focus back to the table + fileTable.requestFocusInWindow(); + } + + /** + * Convert wildcard pattern to regex. + * * matches any characters + * ? matches single character + */ + private String wildcardToRegex(String wildcard) { + StringBuilder regex = new StringBuilder("^"); + for (int i = 0; i < wildcard.length(); i++) { + char c = wildcard.charAt(i); + switch (c) { + case '*': + regex.append(".*"); + break; + case '?': + regex.append("."); + break; + case '.': + regex.append("\\."); + break; + case '\\': + regex.append("\\\\"); + break; + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': + case '^': + case '$': + case '|': + case '+': + regex.append("\\").append(c); + break; + default: + regex.append(c); + break; + } + } + regex.append("$"); + return regex.toString(); + } + public void copyToClipboard(boolean cut) { List selected = getSelectedItems(); if (selected.isEmpty()) return; diff --git a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java index 5f6a04e..120ee81 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java +++ b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java @@ -205,6 +205,9 @@ public class MainWindow extends JFrame { leftPanel.getFileTable().addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { + activePanel = leftPanel; + updateActivePanelBorder(); + updateCommandLinePrompt(); // Ensure some row is selected JTable leftTable = leftPanel.getFileTable(); if (leftTable.getSelectedRow() == -1 && leftTable.getRowCount() > 0) { @@ -222,6 +225,9 @@ public class MainWindow extends JFrame { rightPanel.getFileTable().addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { + activePanel = rightPanel; + updateActivePanelBorder(); + updateCommandLinePrompt(); // Ensure some row is selected JTable rightTable = rightPanel.getFileTable(); if (rightTable.getSelectedRow() == -1 && rightTable.getRowCount() > 0) { @@ -285,8 +291,12 @@ public class MainWindow extends JFrame { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { - tf.setText(""); - activePanel.getFileTable().requestFocusInWindow(); + if (!tf.getText().isEmpty()) { + tf.setText(""); + if (activePanel != null && activePanel.getFileTable() != null) { + activePanel.getFileTable().requestFocusInWindow(); + } + } e.consume(); } else if (e.getKeyCode() == KeyEvent.VK_TAB) { activePanel.getFileTable().requestFocusInWindow(); @@ -613,6 +623,10 @@ public class MainWindow extends JFrame { JMenuItem searchItem = new JMenuItem("Search..."); searchItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_DOWN_MASK)); searchItem.addActionListener(e -> showSearchDialog()); + + JMenuItem selectWildcardItem = new JMenuItem("Select by wildcard..."); + selectWildcardItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK)); + selectWildcardItem.addActionListener(e -> showWildcardSelectDialog()); JMenuItem refreshItem = new JMenuItem("Refresh"); refreshItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F5, InputEvent.CTRL_DOWN_MASK)); @@ -623,6 +637,7 @@ public class MainWindow extends JFrame { exitItem.addActionListener(e -> saveConfigAndExit()); fileMenu.add(searchItem); + fileMenu.add(selectWildcardItem); fileMenu.add(refreshItem); fileMenu.addSeparator(); fileMenu.add(exitItem); @@ -824,10 +839,12 @@ public class MainWindow extends JFrame { // ESC - global escape to return focus to panels rootPane.registerKeyboardAction(e -> { - if (activePanel != null) { - // Always clear command line and return focus to panels + Object currentItem = commandLine.getEditor().getItem(); + if (currentItem != null && !currentItem.toString().isEmpty()) { commandLine.getEditor().setItem(""); - activePanel.getFileTable().requestFocusInWindow(); + if (activePanel != null && activePanel.getFileTable() != null) { + activePanel.getFileTable().requestFocusInWindow(); + } } }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); @@ -919,6 +936,11 @@ public class MainWindow extends JFrame { rootPane.registerKeyboardAction(e -> showCommandLineHistory(), KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK), JComponent.WHEN_IN_FOCUSED_WINDOW); + + // Ctrl+A - Select files by wildcard + rootPane.registerKeyboardAction(e -> showWildcardSelectDialog(), + KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK), + JComponent.WHEN_IN_FOCUSED_WINDOW); } /** @@ -1001,6 +1023,8 @@ public class MainWindow extends JFrame { * Attach TAB handling to switch panels */ private void addTabKeyHandler(JTable table) { + if (table == null) return; + // Remove standard Swing TAB behavior table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) .put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "switchPanel"); @@ -1072,6 +1096,16 @@ public class MainWindow extends JFrame { } } }); + + // Wildcard selection (Ctrl+A) + table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) + .put(KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK), "wildcardSelect"); + table.getActionMap().put("wildcardSelect", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + showWildcardSelectDialog(); + } + }); } /** @@ -1419,6 +1453,26 @@ public class MainWindow extends JFrame { dialog.setVisible(true); } + /** + * Show wildcard select dialog + */ + public void showWildcardSelectDialog() { + if (activePanel == null) return; + + WildcardSelectDialog dialog = new WildcardSelectDialog(this); + dialog.setVisible(true); + + String pattern = dialog.getPattern(); + if (pattern != null && !pattern.isEmpty()) { + activePanel.selectByWildcard(pattern); + } else { + // If cancelled, return focus to the active panel + if (activePanel.getFileTable() != null) { + activePanel.getFileTable().requestFocusInWindow(); + } + } + } + /** * Show the given file's parent directory in the panel that currently has focus * and select the file in that panel. diff --git a/src/main/java/cz/kamma/kfmanager/ui/WildcardSelectDialog.java b/src/main/java/cz/kamma/kfmanager/ui/WildcardSelectDialog.java new file mode 100644 index 0000000..c9ee1c7 --- /dev/null +++ b/src/main/java/cz/kamma/kfmanager/ui/WildcardSelectDialog.java @@ -0,0 +1,89 @@ +package cz.kamma.kfmanager.ui; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; + +/** + * Dialog for entering a wildcard pattern to select matching files + */ +public class WildcardSelectDialog extends JDialog { + + private JTextField patternField; + private boolean confirmed = false; + + public WildcardSelectDialog(Frame parent) { + super(parent, "Vybrat podle masky", true); + initComponents(); + pack(); + setLocationRelativeTo(parent); + } + + private void initComponents() { + setLayout(new BorderLayout(10, 10)); + + // Main panel with padding + JPanel mainPanel = new JPanel(new BorderLayout(5, 5)); + mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + + // Label + JLabel label = new JLabel("Zadejte masku (např. *.txt, test*.*, file?.dat):"); + mainPanel.add(label, BorderLayout.NORTH); + + // Text field for pattern + patternField = new JTextField("*", 30); + patternField.selectAll(); + mainPanel.add(patternField, BorderLayout.CENTER); + + add(mainPanel, BorderLayout.CENTER); + + // Button panel + JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); + + JButton okButton = new JButton("OK"); + okButton.addActionListener(e -> { + confirmed = true; + dispose(); + }); + + JButton cancelButton = new JButton("Zrušit"); + cancelButton.addActionListener(e -> { + confirmed = false; + dispose(); + }); + + buttonPanel.add(okButton); + buttonPanel.add(cancelButton); + + add(buttonPanel, BorderLayout.SOUTH); + + // Enter confirms + patternField.addActionListener(e -> { + confirmed = true; + dispose(); + }); + + // ESC cancels + getRootPane().registerKeyboardAction(e -> { + confirmed = false; + dispose(); + }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); + + getRootPane().setDefaultButton(okButton); + } + + public String getPattern() { + return confirmed ? patternField.getText().trim() : null; + } + + @Override + public void setVisible(boolean visible) { + if (visible) { + SwingUtilities.invokeLater(() -> { + patternField.requestFocusInWindow(); + patternField.selectAll(); + }); + } + super.setVisible(visible); + } +}