associate with added
This commit is contained in:
parent
f88a6c10a1
commit
0d051f240d
@ -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
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -17,12 +17,17 @@ public class FilePanel extends JPanel {
|
||||
private JComboBox<File> 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<File> 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
|
||||
@ -548,6 +604,11 @@ public class FilePanel extends JPanel {
|
||||
return tab != null ? tab.getCurrentDirectory() : null;
|
||||
}
|
||||
|
||||
public String getCurrentPath() {
|
||||
File dir = getCurrentDirectory();
|
||||
return dir != null ? dir.getAbsolutePath() : "";
|
||||
}
|
||||
|
||||
public List<FileItem> getSelectedItems() {
|
||||
FilePanelTab tab = getCurrentTab();
|
||||
return tab != null ? tab.getSelectedItems() : java.util.Collections.emptyList();
|
||||
|
||||
@ -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<String, String> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ public class MainWindow extends JFrame {
|
||||
private JPanel buttonPanel;
|
||||
private JToolBar toolBar;
|
||||
private JComboBox<String> 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();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -905,6 +915,16 @@ public class MainWindow extends JFrame {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1526,73 +1555,20 @@ public class MainWindow extends JFrame {
|
||||
// Add to history
|
||||
addCommandToHistory(command.trim());
|
||||
|
||||
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);
|
||||
// Final prompt update after command execution (path might have changed)
|
||||
updateCommandLinePrompt();
|
||||
|
||||
// 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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user