From 31715240b6be749eeef6a00d06664887a7d58f6d Mon Sep 17 00:00:00 2001 From: Radek Davidek Date: Wed, 4 Mar 2026 19:21:53 +0100 Subject: [PATCH] command history rotation --- .../cz/kamma/kfmanager/ui/MainWindow.java | 141 ++++++++++++++++-- 1 file changed, 130 insertions(+), 11 deletions(-) diff --git a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java index 4d26270..545119d 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java +++ b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java @@ -37,6 +37,7 @@ public class MainWindow extends JFrame { private int lastMainPanelExtent = -1; private boolean applyingSavedDividerLocation = false; private boolean wildcardDialogOpen = false; + private int commandHistoryIndex = -1; public MainWindow() { super("KF Manager v" + MainApp.APP_VERSION + " (" + MainApp.CURRENT_OS + ")"); @@ -390,6 +391,8 @@ public class MainWindow extends JFrame { } else if (e.getKeyCode() == KeyEvent.VK_E && e.isControlDown()) { showCommandLineHistory(); e.consume(); + } else if (!e.isControlDown() && !e.isAltDown() && !e.isMetaDown()) { + commandHistoryIndex = -1; } } }); @@ -1233,6 +1236,7 @@ public class MainWindow extends JFrame { Object currentItem = commandLine.getEditor().getItem(); if (currentItem != null && !currentItem.toString().isEmpty()) { commandLine.getEditor().setItem(""); + commandHistoryIndex = -1; textCleared = true; } @@ -2347,15 +2351,14 @@ public class MainWindow extends JFrame { private void showCommandLineHistory() { if (commandLine != null && commandLine.getItemCount() > 0) { commandLine.requestFocusInWindow(); - if (!commandLine.isPopupVisible()) { - commandLine.setSelectedIndex(0); - commandLine.showPopup(); - } else { - int count = commandLine.getItemCount(); - int current = commandLine.getSelectedIndex(); - // If index is -1 or invalid, start from 0, otherwise go to next - int nextIndex = (current < 0) ? 0 : (current + 1) % count; - commandLine.setSelectedIndex(nextIndex); + int count = commandLine.getItemCount(); + commandHistoryIndex = (commandHistoryIndex + 1) % count; + String nextCommand = commandLine.getItemAt(commandHistoryIndex); + commandLine.getEditor().setItem(nextCommand); + + Component editorComp = commandLine.getEditor().getEditorComponent(); + if (editorComp instanceof JTextField tf) { + tf.setCaretPosition(tf.getText().length()); } } } @@ -2554,8 +2557,14 @@ public class MainWindow extends JFrame { // Special handling for bash/sh to open a terminal with that shell openTerminal(trimmed.toLowerCase()); } else { - // Execute natively for other commands - executeNative(trimmed, null); + // On Linux, run command line commands in an external terminal. + // This keeps command output visible and uses the active panel directory. + if (MainApp.CURRENT_OS == MainApp.OS.LINUX) { + openTerminalWithCommand(trimmed); + } else { + // Execute natively for other commands + executeNative(trimmed, null); + } } // Final prompt update after command execution (path might have changed) @@ -2645,6 +2654,115 @@ public class MainWindow extends JFrame { } } + private void openTerminalWithCommand(String command) { + if (command == null || command.trim().isEmpty()) return; + + File currentDir = activePanel.getCurrentDirectory(); + if (currentDir == null || !currentDir.exists()) { + currentDir = new File(System.getProperty("user.home")); + } + + // Keep terminal open after running command so user can inspect output. + String shellScript = command + "; exec sh -i"; + + try { + List terminals = new ArrayList<>(); + + String envTerminal = System.getenv("TERMINAL"); + if (envTerminal != null && !envTerminal.isEmpty()) { + terminals.add(envTerminal); + } + + terminals.add("xdg-terminal-exec"); + terminals.addAll(Arrays.asList( + "x-terminal-emulator", + "gnome-terminal", + "konsole", + "xfce4-terminal", + "alacritty", + "kitty", + "foot", + "wezterm", + "mate-terminal", + "terminator", + "tilix", + "qterminal", + "urxvt", + "st", + "xterm" + )); + + boolean started = false; + for (String terminal : terminals) { + try { + List args = new ArrayList<>(); + args.add(terminal); + + if (terminal.equals("gnome-terminal") || terminal.equals("xfce4-terminal") || + terminal.equals("mate-terminal") || terminal.equals("terminator") || + terminal.equals("tilix")) { + args.add("--working-directory=" + currentDir.getAbsolutePath()); + args.add("--"); + args.add("sh"); + args.add("-c"); + args.add(shellScript); + } else if (terminal.equals("alacritty") || terminal.equals("foot")) { + args.add("--working-directory"); + args.add(currentDir.getAbsolutePath()); + args.add("-e"); + args.add("sh"); + args.add("-c"); + args.add(shellScript); + } else if (terminal.equals("kitty")) { + args.add("--directory"); + args.add(currentDir.getAbsolutePath()); + args.add("sh"); + args.add("-c"); + args.add(shellScript); + } else if (terminal.equals("konsole") || terminal.equals("qterminal")) { + args.add("--workdir"); + args.add(currentDir.getAbsolutePath()); + args.add("-e"); + args.add("sh"); + args.add("-c"); + args.add(shellScript); + } else if (terminal.equals("wezterm")) { + args.add("start"); + args.add("--cwd"); + args.add(currentDir.getAbsolutePath()); + args.add("sh"); + args.add("-c"); + args.add(shellScript); + } else if (terminal.equals("xdg-terminal-exec")) { + args.add("sh"); + args.add("-c"); + args.add(shellScript); + } else { + args.add("-e"); + args.add("sh"); + args.add("-c"); + args.add(shellScript); + } + + new ProcessBuilder(args).directory(currentDir).start(); + started = true; + break; + } catch (IOException ignore) { + // Try next terminal command. + } + } + + if (!started) { + throw new Exception("Could not start any terminal emulator."); + } + } catch (Exception e) { + JOptionPane.showMessageDialog(this, + "Error opening terminal for command execution: " + e.getMessage(), + "Error", + JOptionPane.ERROR_MESSAGE); + } + } + private List parseCommand(String command) { List list = new ArrayList<>(); StringBuilder sb = new StringBuilder(); @@ -2679,6 +2797,7 @@ public class MainWindow extends JFrame { } } commandLine.insertItemAt(command, 0); + commandHistoryIndex = -1; // We don't necessarily want to select it here as it might interfere with the editor state }