From 0d051f240d7a75912657bbf207d7852120f3bbb8 Mon Sep 17 00:00:00 2001 From: Radek Davidek Date: Fri, 16 Jan 2026 18:21:16 +0100 Subject: [PATCH] associate with added --- src/main/java/com/kfmanager/MainApp.java | 2 +- .../java/com/kfmanager/config/AppConfig.java | 7 ++ src/main/java/com/kfmanager/ui/FilePanel.java | 75 ++++++++++-- .../java/com/kfmanager/ui/FilePanelTab.java | 48 ++++++++ .../java/com/kfmanager/ui/MainWindow.java | 114 +++++++----------- 5 files changed, 169 insertions(+), 77 deletions(-) diff --git a/src/main/java/com/kfmanager/MainApp.java b/src/main/java/com/kfmanager/MainApp.java index 7a42938..92985ce 100644 --- a/src/main/java/com/kfmanager/MainApp.java +++ b/src/main/java/com/kfmanager/MainApp.java @@ -9,7 +9,7 @@ import javax.swing.*; */ public class MainApp { - public static final String APP_VERSION = "0.0.3"; + public static final String APP_VERSION = "0.0.4"; public static void main(String[] args) { // Set look and feel to system default diff --git a/src/main/java/com/kfmanager/config/AppConfig.java b/src/main/java/com/kfmanager/config/AppConfig.java index e4fe840..2969e3c 100644 --- a/src/main/java/com/kfmanager/config/AppConfig.java +++ b/src/main/java/com/kfmanager/config/AppConfig.java @@ -275,6 +275,13 @@ public class AppConfig { } } + /** Add or update a single file association */ + public void setFileAssociation(String pattern, String command) { + if (pattern == null || pattern.trim().isEmpty()) return; + properties.setProperty("assoc." + pattern.trim(), command); + saveConfig(); + } + // --- Appearance (global) settings --- public String getGlobalFontName() { return properties.getProperty("global.font.name", "Monospaced"); diff --git a/src/main/java/com/kfmanager/ui/FilePanel.java b/src/main/java/com/kfmanager/ui/FilePanel.java index 91ffc22..48c6993 100644 --- a/src/main/java/com/kfmanager/ui/FilePanel.java +++ b/src/main/java/com/kfmanager/ui/FilePanel.java @@ -17,12 +17,17 @@ public class FilePanel extends JPanel { private JComboBox driveCombo; private JLabel driveInfoLabel; private com.kfmanager.config.AppConfig appConfig; + private Runnable onDirectoryChangedAll; public FilePanel(String initialPath) { initComponents(); addNewTab(initialPath); } + public void setOnDirectoryChangedAll(Runnable callback) { + this.onDirectoryChangedAll = callback; + } + /** Start inline rename on the currently selected tab/table. */ public void startInlineRename() { FilePanelTab tab = getCurrentTab(); @@ -217,7 +222,10 @@ public class FilePanel extends JPanel { if (appConfig != null) tab.setAppConfig(appConfig); // Set callback for updating tab title on directory change - tab.setOnDirectoryChanged(() -> updateTabTitle(tab)); + tab.setOnDirectoryChanged(() -> { + updateTabTitle(tab); + if (onDirectoryChangedAll != null) onDirectoryChangedAll.run(); + }); // Forward switchPanel callback to the tab so TAB works from any tab tab.setOnSwitchPanelRequested(switchPanelCallback); @@ -253,7 +261,10 @@ public class FilePanel extends JPanel { public void addNewTabWithMode(String path, ViewMode mode) { FilePanelTab tab = new FilePanelTab(path); if (appConfig != null) tab.setAppConfig(appConfig); - tab.setOnDirectoryChanged(() -> updateTabTitle(tab)); + tab.setOnDirectoryChanged(() -> { + updateTabTitle(tab); + if (onDirectoryChangedAll != null) onDirectoryChangedAll.run(); + }); tab.setOnSwitchPanelRequested(switchPanelCallback); if (mode != null) { @@ -352,15 +363,60 @@ public class FilePanel extends JPanel { private void populateDrives() { driveCombo.removeAllItems(); + java.util.Set driveSet = new java.util.LinkedHashSet<>(); + + // Add standard roots File[] roots = File.listRoots(); if (roots != null) { for (File r : roots) { - try { - driveCombo.addItem(r); - } catch (Exception ignore) {} + driveSet.add(r); } - // select first drive by default - if (roots.length > 0) driveCombo.setSelectedItem(roots[0]); + } + + // Add home directory + driveSet.add(new File(System.getProperty("user.home"))); + + // On Linux/Unix, add common mount points + String os = System.getProperty("os.name").toLowerCase(); + if (!os.contains("win")) { + // Common Linux/Unix mount points + String user = System.getProperty("user.name"); + + // Typical locations for removable media + String[] commonMountPoints = { + "/media/" + user, + "/run/media/" + user, + "/mnt", + "/Volumes" // macOS + }; + + for (String path : commonMountPoints) { + File dir = new File(path); + if (dir.exists() && dir.isDirectory()) { + File[] subDirs = dir.listFiles(); + if (subDirs != null) { + for (File sub : subDirs) { + if (sub.isDirectory()) { + driveSet.add(sub); + } + } + } else if (!path.contains(user)) { + // For /mnt we might also add the directory itself if it's empty but meant to be used + driveSet.add(dir); + } + } + } + } + + for (File d : driveSet) { + try { + driveCombo.addItem(d); + } catch (Exception ignore) {} + } + + // Initialize selection + if (driveCombo.getItemCount() > 0) { + driveCombo.setSelectedIndex(0); } // Update info for currently selected drive @@ -547,6 +603,11 @@ public class FilePanel extends JPanel { FilePanelTab tab = getCurrentTab(); return tab != null ? tab.getCurrentDirectory() : null; } + + public String getCurrentPath() { + File dir = getCurrentDirectory(); + return dir != null ? dir.getAbsolutePath() : ""; + } public List getSelectedItems() { FilePanelTab tab = getCurrentTab(); diff --git a/src/main/java/com/kfmanager/ui/FilePanelTab.java b/src/main/java/com/kfmanager/ui/FilePanelTab.java index b0f80d5..5d432b9 100644 --- a/src/main/java/com/kfmanager/ui/FilePanelTab.java +++ b/src/main/java/com/kfmanager/ui/FilePanelTab.java @@ -1149,6 +1149,14 @@ public class FilePanelTab extends JPanel { }); menu.add(copyPath); + // Associate with... + JMenuItem associateItem = new JMenuItem("Associate with..."); + associateItem.addActionListener(ae -> { + if (item.isDirectory() || item.getName().equals("..")) return; + showAssociationDialog(item.getFile()); + }); + menu.add(associateItem); + // Delete JMenuItem deleteItem = new JMenuItem("Delete"); deleteItem.addActionListener(ae -> { @@ -2259,4 +2267,44 @@ public class FilePanelTab extends JPanel { return 16; } } + + private void showAssociationDialog(File file) { + String name = file.getName(); + String ext = ""; + int lastDot = name.lastIndexOf('.'); + if (lastDot > 0 && lastDot < name.length() - 1) { + ext = name.substring(lastDot + 1); + } else { + ext = name; // fallback to full name if no extension + } + + String pattern = JOptionPane.showInputDialog(this, + "File pattern (e.g. *.txt or txt):", + ext); + + if (pattern == null || pattern.trim().isEmpty()) return; + + // If user just provided an extension like "txt", convert it to "*.txt" internally for consistency + String normalizedPattern = pattern.trim(); + if (!normalizedPattern.contains("*") && !normalizedPattern.contains("?")) { + normalizedPattern = "*." + normalizedPattern; + } + + String existingCmd = ""; + if (persistedConfig != null) { + java.util.Map associations = persistedConfig.getFileAssociations(); + existingCmd = associations.getOrDefault(normalizedPattern, ""); + } + + String command = JOptionPane.showInputDialog(this, + "Command to run (%f for full path, %n for name):", + existingCmd); + + if (command != null && !command.trim().isEmpty()) { + if (persistedConfig != null) { + persistedConfig.setFileAssociation(normalizedPattern, command.trim()); + JOptionPane.showMessageDialog(this, "Association saved for " + normalizedPattern); + } + } + } } diff --git a/src/main/java/com/kfmanager/ui/MainWindow.java b/src/main/java/com/kfmanager/ui/MainWindow.java index 52e3add..864c547 100644 --- a/src/main/java/com/kfmanager/ui/MainWindow.java +++ b/src/main/java/com/kfmanager/ui/MainWindow.java @@ -25,6 +25,7 @@ public class MainWindow extends JFrame { private JPanel buttonPanel; private JToolBar toolBar; private JComboBox commandLine; + private JLabel cmdLabel; private AppConfig config; public MainWindow() { @@ -73,6 +74,7 @@ public class MainWindow extends JFrame { leftPanel.setBorder(BorderFactory.createTitledBorder("Left panel")); // Provide a callback so tabs inside the panel can request switching panels with TAB leftPanel.setSwitchPanelCallback(() -> switchPanelsFromChild()); + leftPanel.setOnDirectoryChangedAll(() -> updateCommandLinePrompt()); // Load and set ViewMode for left panel try { @@ -89,6 +91,7 @@ public class MainWindow extends JFrame { rightPanel.setBorder(BorderFactory.createTitledBorder("Right panel")); // Provide a callback so tabs inside the panel can request switching panels with TAB rightPanel.setSwitchPanelCallback(() -> switchPanelsFromChild()); + rightPanel.setOnDirectoryChangedAll(() -> updateCommandLinePrompt()); // Load and set ViewMode for right panel try { @@ -106,6 +109,7 @@ public class MainWindow extends JFrame { // Set left panel as active by default activePanel = leftPanel; updateActivePanelBorder(); + updateCommandLinePrompt(); // Restore saved tabs for both panels if present in configuration try { @@ -153,11 +157,13 @@ public class MainWindow extends JFrame { updateActivePanelBorder(); leftPanel.getFileTable().repaint(); rightPanel.getFileTable().repaint(); + updateCommandLinePrompt(); } else if (SwingUtilities.isDescendingFrom(focused, rightPanel)) { activePanel = rightPanel; updateActivePanelBorder(); leftPanel.getFileTable().repaint(); rightPanel.getFileTable().repaint(); + updateCommandLinePrompt(); } } }); @@ -226,7 +232,7 @@ public class MainWindow extends JFrame { 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 = new JLabel(System.getProperty("user.name") + ":" + (activePanel != null ? activePanel.getCurrentDirectory().getAbsolutePath() : "") + ">"); cmdLabel.setFont(new Font("Monospaced", Font.BOLD, 12)); cmdPanel.add(cmdLabel, BorderLayout.WEST); @@ -252,6 +258,9 @@ public class MainWindow extends JFrame { } else if (e.getKeyCode() == KeyEvent.VK_TAB) { activePanel.getFileTable().requestFocusInWindow(); e.consume(); + } else if (e.getKeyCode() == KeyEvent.VK_E && e.isControlDown()) { + showCommandLineHistory(); + e.consume(); } } }); @@ -896,6 +905,7 @@ public class MainWindow extends JFrame { JTable rt = rightPanel.getFileTable(); if (lt != null) lt.repaint(); if (rt != null) rt.repaint(); + updateCommandLinePrompt(); } /** @@ -904,6 +914,16 @@ public class MainWindow extends JFrame { public void switchPanelsFromChild() { switchPanels(); } + + private void updateCommandLinePrompt() { + if (cmdLabel == null) return; + + FilePanel active = activePanel; + if (active == null) return; + + String path = active.getCurrentPath(); + cmdLabel.setText(path + ">"); + } /** * Attach TAB handling to switch panels @@ -1430,9 +1450,18 @@ public class MainWindow extends JFrame { * Show history of command line */ private void showCommandLineHistory() { - if (commandLine != null) { + if (commandLine != null && commandLine.getItemCount() > 0) { commandLine.requestFocusInWindow(); - commandLine.showPopup(); + 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); + } } } @@ -1525,74 +1554,21 @@ public class MainWindow extends JFrame { // Add to history addCommandToHistory(command.trim()); + + // Final prompt update after command execution (path might have changed) + updateCommandLinePrompt(); - 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(); - - // Clear after execution - Component editorComp = commandLine.getEditor().getEditorComponent(); - if (editorComp instanceof JTextField) { - ((JTextField) editorComp).setText(""); - } else { - commandLine.setSelectedItem(""); - } - activePanel.getFileTable().requestFocusInWindow(); - } - - } catch (Exception e) { - JOptionPane.showMessageDialog(this, - "Error executing command: " + e.getMessage(), - "Error", - JOptionPane.ERROR_MESSAGE); + // Execute natively, not via bash wrapper + executeNative(command.trim(), null); + + // Clear after execution and return focus + Component editorComp = commandLine.getEditor().getEditorComponent(); + if (editorComp instanceof JTextField) { + ((JTextField) editorComp).setText(""); + } else { + commandLine.setSelectedItem(""); } + activePanel.getFileTable().requestFocusInWindow(); } private void executeNative(String command, String workingDir) {