From b4226db038dbd38330b209a2f04d80582d9bdcef Mon Sep 17 00:00:00 2001 From: Radek Davidek Date: Thu, 15 Jan 2026 11:53:33 +0100 Subject: [PATCH] added command line --- src/main/java/com/kfmanager/ui/FilePanel.java | 20 ++ .../java/com/kfmanager/ui/FilePanelTab.java | 15 +- .../java/com/kfmanager/ui/MainWindow.java | 204 +++++++++++++++++- 3 files changed, 235 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/kfmanager/ui/FilePanel.java b/src/main/java/com/kfmanager/ui/FilePanel.java index c6e18ba..c4377fd 100644 --- a/src/main/java/com/kfmanager/ui/FilePanel.java +++ b/src/main/java/com/kfmanager/ui/FilePanel.java @@ -454,6 +454,21 @@ public class FilePanel extends JPanel { } // Delegování metod na aktuální tab + + /** + * Switch to the next tab in this panel. + */ + public void nextTab() { + int count = tabbedPane.getTabCount(); + if (count > 1) { + int nextIndex = (tabbedPane.getSelectedIndex() + 1) % count; + tabbedPane.setSelectedIndex(nextIndex); + FilePanelTab tab = getCurrentTab(); + if (tab != null) { + tab.getFileTable().requestFocusInWindow(); + } + } + } public JTable getFileTable() { FilePanelTab tab = getCurrentTab(); @@ -469,6 +484,11 @@ public class FilePanel extends JPanel { FilePanelTab tab = getCurrentTab(); return tab != null ? tab.getSelectedItems() : java.util.Collections.emptyList(); } + + public FileItem getFocusedItem() { + FilePanelTab tab = getCurrentTab(); + return tab != null ? tab.getFocusedItem() : null; + } public void setViewMode(ViewMode mode) { FilePanelTab tab = getCurrentTab(); diff --git a/src/main/java/com/kfmanager/ui/FilePanelTab.java b/src/main/java/com/kfmanager/ui/FilePanelTab.java index c95ecb5..8164d33 100644 --- a/src/main/java/com/kfmanager/ui/FilePanelTab.java +++ b/src/main/java/com/kfmanager/ui/FilePanelTab.java @@ -301,6 +301,7 @@ public class FilePanelTab extends JPanel { // to ensure no drag gestures or DnD occur. fileTable.setDragEnabled(false); fileTable.setTransferHandler(null); + fileTable.setFocusTraversalKeysEnabled(false); try { fileTable.setDropMode(null); } catch (Exception ignore) { @@ -433,7 +434,7 @@ public class FilePanelTab extends JPanel { fileTable.addKeyListener(new java.awt.event.KeyAdapter() { @Override public void keyPressed(java.awt.event.KeyEvent e) { - if (e.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER) { + if (e.getKeyCode() == java.awt.event.KeyEvent.VK_ENTER && !e.isControlDown()) { openSelectedItem(); e.consume(); } else if (e.getKeyCode() == java.awt.event.KeyEvent.VK_BACK_SPACE) { @@ -1142,6 +1143,18 @@ public class FilePanelTab extends JPanel { return selected; } + + public FileItem getFocusedItem() { + int selectedRow = fileTable.getSelectedRow(); + if (selectedRow >= 0) { + if (viewMode == ViewMode.BRIEF) { + return tableModel.getItemFromBriefLayout(selectedRow, briefCurrentColumn); + } else { + return tableModel.getItem(selectedRow); + } + } + return null; + } public void setViewMode(ViewMode mode) { if (this.viewMode != mode) { diff --git a/src/main/java/com/kfmanager/ui/MainWindow.java b/src/main/java/com/kfmanager/ui/MainWindow.java index be5476f..24af0df 100644 --- a/src/main/java/com/kfmanager/ui/MainWindow.java +++ b/src/main/java/com/kfmanager/ui/MainWindow.java @@ -19,10 +19,11 @@ public class MainWindow extends JFrame { private FilePanel rightPanel; private FilePanel activePanel; private JPanel buttonPanel; + private JTextField commandLine; private AppConfig config; public MainWindow() { - super("KF File Manager"); + super("KF Manager"); // Load configuration config = new AppConfig(); @@ -182,14 +183,68 @@ public class MainWindow extends JFrame { rightPanel.getFileTable().repaint(); } }); + + // Click on panel anywhere should request focus to its table + leftPanel.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + leftPanel.getFileTable().requestFocusInWindow(); + } + }); + rightPanel.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + rightPanel.getFileTable().requestFocusInWindow(); + } + }); // Add TAB handler to switch between panels addTabKeyHandler(leftPanel.getFileTable()); addTabKeyHandler(rightPanel.getFileTable()); - // Bottom panel with buttons + // Add command line focus redirection + addCommandLineRedirect(leftPanel.getFileTable()); + addCommandLineRedirect(rightPanel.getFileTable()); + + // Container for everything below the file panels + JPanel bottomContainer = new JPanel(new BorderLayout()); + + // Command line panel + JPanel cmdPanel = new JPanel(new BorderLayout(5, 0)); + cmdPanel.setBorder(BorderFactory.createEmptyBorder(2, 5, 0, 5)); + + JLabel cmdLabel = new JLabel(System.getProperty("user.name") + ">"); + cmdLabel.setFont(new Font("Monospaced", Font.BOLD, 12)); + cmdPanel.add(cmdLabel, BorderLayout.WEST); + + commandLine = new JTextField(); + commandLine.setFont(new Font("Monospaced", Font.PLAIN, 12)); + commandLine.addActionListener(e -> executeCommand(commandLine.getText())); + + // Let the panels catch focus back if user presses ESC or TAB in command line + commandLine.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { + commandLine.setText(""); + activePanel.getFileTable().requestFocus(); + e.consume(); + } else if (e.getKeyCode() == KeyEvent.VK_TAB) { + activePanel.getFileTable().requestFocus(); + e.consume(); + } + } + }); + + cmdPanel.add(commandLine, BorderLayout.CENTER); + + bottomContainer.add(cmdPanel, BorderLayout.NORTH); + + // Bottom panel with buttons createButtonPanel(); - add(buttonPanel, BorderLayout.SOUTH); + bottomContainer.add(buttonPanel, BorderLayout.SOUTH); + + add(bottomContainer, BorderLayout.SOUTH); // Menu createMenuBar(); @@ -491,6 +546,14 @@ public class MainWindow extends JFrame { KeyStroke.getKeyStroke(KeyEvent.VK_F9, 0), JComponent.WHEN_IN_FOCUSED_WINDOW); + // Ctrl+TAB - switch tabs in active panel + rootPane.registerKeyboardAction(e -> { + if (activePanel != null) { + activePanel.nextTab(); + } + }, KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.CTRL_DOWN_MASK), + JComponent.WHEN_IN_FOCUSED_WINDOW); + // Delete key - global delete binding (also added per-table) rootPane.registerKeyboardAction(e -> deleteFiles(), KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), @@ -621,6 +684,18 @@ public class MainWindow extends JFrame { switchPanels(); } }); + + // Add Ctrl+Tab handling at the table level + table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) + .put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.CTRL_DOWN_MASK), "nextTab"); + table.getActionMap().put("nextTab", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + if (activePanel != null) { + activePanel.nextTab(); + } + } + }); // Přidáme F8 pro mazání s vyšší prioritou než defaultní Swing akce table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) @@ -638,6 +713,63 @@ public class MainWindow extends JFrame { table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) .put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, InputEvent.SHIFT_DOWN_MASK), "deleteFiles"); } + + /** + * Automatically focus command line when user starts typing on a table + */ + private void addCommandLineRedirect(JTable table) { + // Use InputMap/ActionMap for Ctrl+Enter and Ctrl+Shift+Enter as KeyListener might be bypassed by JTable + table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) + .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK), "copyNameToCmd"); + table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) + .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK), "copyPathToCmd"); + + table.getActionMap().put("copyNameToCmd", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + copyFocusedToCommandLine(false); + } + }); + + table.getActionMap().put("copyPathToCmd", new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + copyFocusedToCommandLine(true); + } + }); + + table.addKeyListener(new KeyAdapter() { + @Override + public void keyTyped(KeyEvent e) { + char c = e.getKeyChar(); + // Printable characters only (exclude control keys like Enter, Backspace, Esc, Tab) + if (c != KeyEvent.CHAR_UNDEFINED && c != '\b' && c != '\n' && c != '\t' && c != 27) { + commandLine.requestFocusInWindow(); + commandLine.setText(commandLine.getText() + c); + e.consume(); + } + } + }); + } + + private void copyFocusedToCommandLine(boolean fullPath) { + FileItem focused = activePanel.getFocusedItem(); + if (focused != null && !focused.getName().equals("..")) { + String current = commandLine.getText(); + String toAdd = fullPath ? focused.getFile().getAbsolutePath() : focused.getName(); + + // If it contains spaces, wrap in quotes + if (toAdd.contains(" ")) { + toAdd = "\"" + toAdd + "\""; + } + + if (!current.isEmpty() && !current.endsWith(" ")) { + commandLine.setText(current + " " + toAdd); + } else { + commandLine.setText(current + toAdd); + } + } + } /** * Copy selected files to the opposite panel @@ -1108,6 +1240,72 @@ public class MainWindow extends JFrame { } } + private void executeCommand(String command) { + if (command == null || command.trim().isEmpty()) { + return; + } + + File currentDir = activePanel.getCurrentDirectory(); + if (currentDir == null) { + currentDir = new File(System.getProperty("user.home")); + } + + try { + String osName = System.getProperty("os.name").toLowerCase(); + ProcessBuilder pb; + + if (osName.contains("win")) { + // Windows: cmd /c command && pause + pb = new ProcessBuilder("cmd.exe", "/c", "start", "cmd.exe", "/k", command); + } else if (osName.contains("mac")) { + // macOS: Open terminal and execute + String appleScript = String.format( + "tell application \"Terminal\" to do script \"cd '%s' && %s\"", + currentDir.getAbsolutePath(), command.replace("\"", "\\\"") + ); + pb = new ProcessBuilder("osascript", "-e", appleScript); + } else { + // Linux: Try common terminals with -e or --command + String[] terminals = {"gnome-terminal", "konsole", "xfce4-terminal", "mate-terminal", "xterm"}; + pb = null; + + for (String terminal : terminals) { + try { + Process p = Runtime.getRuntime().exec(new String[]{"which", terminal}); + if (p.waitFor() == 0) { + if (terminal.equals("gnome-terminal") || terminal.equals("xfce4-terminal") || terminal.equals("mate-terminal")) { + pb = new ProcessBuilder(terminal, "--working-directory=" + currentDir.getAbsolutePath(), "--", "bash", "-c", command + "; echo; echo 'Press Enter to close...'; read"); + } else if (terminal.equals("konsole")) { + pb = new ProcessBuilder(terminal, "--workdir", currentDir.getAbsolutePath(), "-e", "bash", "-c", command + "; echo; echo 'Press Enter to close...'; read"); + } else { + pb = new ProcessBuilder(terminal, "-e", "bash -c \"" + command + "; echo; echo 'Press Enter to close...'; read\""); + } + break; + } + } catch (Exception e) { + // try next + } + } + + if (pb == null) { + pb = new ProcessBuilder("xterm", "-e", "bash -c \"" + command + "; echo; echo 'Press Enter to close...'; read\""); + } + } + + if (pb != null) { + pb.directory(currentDir); + pb.start(); + commandLine.setText(""); // Clear after execution + } + + } catch (Exception e) { + JOptionPane.showMessageDialog(this, + "Chyba při spouštění příkazu: " + e.getMessage(), + "Chyba", + JOptionPane.ERROR_MESSAGE); + } + } + /** * Show About dialog */