new system for associations
This commit is contained in:
parent
5aeba21e9a
commit
a4afc103ad
@ -367,42 +367,57 @@ public class AppConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// --- File Associations ---
|
||||
/** Returns a map of pattern (extension or wildcard) -> command */
|
||||
public java.util.Map<String, String> getFileAssociations() {
|
||||
java.util.Map<String, String> map = new java.util.HashMap<>();
|
||||
for (String key : properties.stringPropertyNames()) {
|
||||
if (key.startsWith("assoc.")) {
|
||||
String pattern = key.substring(6);
|
||||
map.put(pattern, properties.getProperty(key));
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
// --- Open With Configuration ---
|
||||
public static class OpenWithEntry {
|
||||
public String label;
|
||||
public String type; // "directory" or extension
|
||||
public String command;
|
||||
|
||||
public void setFileAssociations(java.util.Map<String, String> associations) {
|
||||
// Remove all current associations first
|
||||
for (String key : properties.stringPropertyNames()) {
|
||||
if (key.startsWith("assoc.")) {
|
||||
properties.remove(key);
|
||||
}
|
||||
}
|
||||
// Add new ones
|
||||
if (associations != null) {
|
||||
for (java.util.Map.Entry<String, String> entry : associations.entrySet()) {
|
||||
String pattern = entry.getKey().trim();
|
||||
if (!pattern.isEmpty()) {
|
||||
properties.setProperty("assoc." + pattern, entry.getValue());
|
||||
}
|
||||
}
|
||||
public OpenWithEntry() {}
|
||||
public OpenWithEntry(String label, String type, String command) {
|
||||
this.label = label;
|
||||
this.type = type;
|
||||
this.command = command;
|
||||
}
|
||||
}
|
||||
|
||||
/** 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();
|
||||
public java.util.List<OpenWithEntry> getOpenWithEntries() {
|
||||
java.util.List<OpenWithEntry> list = new java.util.ArrayList<>();
|
||||
String countStr = properties.getProperty("openwith.count", "0");
|
||||
int count = Integer.parseInt(countStr);
|
||||
for (int i = 0; i < count; i++) {
|
||||
String label = properties.getProperty("openwith." + i + ".label");
|
||||
String type = properties.getProperty("openwith." + i + ".type");
|
||||
String command = properties.getProperty("openwith." + i + ".command");
|
||||
if (label != null && type != null && command != null) {
|
||||
list.add(new OpenWithEntry(label, type, command));
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public void setOpenWithEntries(java.util.List<OpenWithEntry> entries) {
|
||||
// Remove old entries
|
||||
String countStr = properties.getProperty("openwith.count", "0");
|
||||
int count = Integer.parseInt(countStr);
|
||||
for (int i = 0; i < count; i++) {
|
||||
properties.remove("openwith." + i + ".label");
|
||||
properties.remove("openwith." + i + ".type");
|
||||
properties.remove("openwith." + i + ".command");
|
||||
}
|
||||
|
||||
if (entries == null) {
|
||||
properties.setProperty("openwith.count", "0");
|
||||
return;
|
||||
}
|
||||
|
||||
properties.setProperty("openwith.count", String.valueOf(entries.size()));
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
OpenWithEntry e = entries.get(i);
|
||||
properties.setProperty("openwith." + i + ".label", e.label != null ? e.label : "");
|
||||
properties.setProperty("openwith." + i + ".type", e.type != null ? e.type : "");
|
||||
properties.setProperty("openwith." + i + ".command", e.command != null ? e.command : "");
|
||||
}
|
||||
}
|
||||
|
||||
// --- Appearance (global) settings ---
|
||||
|
||||
@ -1346,48 +1346,22 @@ public class FilePanelTab extends JPanel {
|
||||
|
||||
private void openFileNative(File file) {
|
||||
try {
|
||||
// 1. Check custom associations from config (wildcard matching)
|
||||
// 1. Check custom Open with entries from config
|
||||
if (persistedConfig != null) {
|
||||
java.util.Map<String, String> associations = persistedConfig.getFileAssociations();
|
||||
String name = file.getName();
|
||||
java.util.List<cz.kamma.kfmanager.config.AppConfig.OpenWithEntry> owEntries = persistedConfig.getOpenWithEntries();
|
||||
String name = file.getName().toLowerCase();
|
||||
String ext = "";
|
||||
int dot = name.lastIndexOf('.');
|
||||
if (dot > 0 && dot < name.length() - 1) {
|
||||
ext = name.substring(dot + 1);
|
||||
}
|
||||
|
||||
String command = null;
|
||||
|
||||
// Sort associations by length descending to match more specific patterns first (e.g. *.tar.gz before *.gz)
|
||||
java.util.List<String> patterns = new java.util.ArrayList<>(associations.keySet());
|
||||
patterns.sort((a, b) -> Integer.compare(b.length(), a.length()));
|
||||
|
||||
for (String pattern : patterns) {
|
||||
String fullCommand = associations.get(pattern);
|
||||
// Support multiple patterns separated by semicolon (e.g. "jpg;png;gif")
|
||||
String[] subPatterns = pattern.split(";");
|
||||
boolean matched = false;
|
||||
|
||||
for (String sub : subPatterns) {
|
||||
String glob = sub.trim();
|
||||
if (glob.isEmpty()) continue;
|
||||
|
||||
// If user just provided an extension like "txt", convert to "*.txt"
|
||||
if (!glob.contains("*") && !glob.contains("?")) {
|
||||
glob = "*." + glob;
|
||||
for (cz.kamma.kfmanager.config.AppConfig.OpenWithEntry entry : owEntries) {
|
||||
if (entry.type != null && entry.type.equalsIgnoreCase(ext)) {
|
||||
command = entry.command;
|
||||
break; // Use the first matching entry
|
||||
}
|
||||
|
||||
try {
|
||||
java.nio.file.PathMatcher matcher = java.nio.file.FileSystems.getDefault().getPathMatcher("glob:" + glob);
|
||||
if (matcher.matches(java.nio.file.Paths.get(name))) {
|
||||
command = fullCommand;
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
// Fallback for simple extension match if glob is invalid
|
||||
if (name.toLowerCase().endsWith("." + glob.toLowerCase())) {
|
||||
command = fullCommand;
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (matched) break;
|
||||
}
|
||||
|
||||
if (command != null && !command.trim().isEmpty()) {
|
||||
@ -1612,14 +1586,6 @@ public class FilePanelTab extends JPanel {
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
// 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 -> {
|
||||
@ -3026,44 +2992,4 @@ public class FilePanelTab extends JPanel {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void showAssociationDialog(File file) {
|
||||
String name = file.getName();
|
||||
String initialPattern = "";
|
||||
int lastDot = name.lastIndexOf('.');
|
||||
if (lastDot > 0 && lastDot < name.length() - 1) {
|
||||
initialPattern = "*." + name.substring(lastDot + 1).toLowerCase();
|
||||
} else {
|
||||
initialPattern = name; // fallback to full name if no extension
|
||||
}
|
||||
|
||||
String pattern = JOptionPane.showInputDialog(this,
|
||||
"File pattern (e.g. *.txt or txt):",
|
||||
initialPattern);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@ public class SettingsDialog extends JDialog {
|
||||
private final String originalExternalEditorPath;
|
||||
private final int originalToolbarButtonSize;
|
||||
private final int originalToolbarIconSize;
|
||||
private final java.util.Map<String, String> originalFileAssociations;
|
||||
private final java.util.List<cz.kamma.kfmanager.config.AppConfig.OpenWithEntry> originalOpenWithEntries;
|
||||
|
||||
// Appearance controls
|
||||
private JButton appearanceFontBtn;
|
||||
@ -60,7 +60,7 @@ public class SettingsDialog extends JDialog {
|
||||
this.originalExternalEditorPath = config.getExternalEditorPath();
|
||||
this.originalToolbarButtonSize = config.getToolbarButtonSize();
|
||||
this.originalToolbarIconSize = config.getToolbarIconSize();
|
||||
this.originalFileAssociations = new java.util.HashMap<>(config.getFileAssociations());
|
||||
this.originalOpenWithEntries = new java.util.ArrayList<>(config.getOpenWithEntries());
|
||||
|
||||
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||
setSize(700, 420);
|
||||
@ -73,7 +73,7 @@ public class SettingsDialog extends JDialog {
|
||||
model.addElement("Behavior");
|
||||
model.addElement("Sorting");
|
||||
model.addElement("Toolbar");
|
||||
model.addElement("Associations");
|
||||
model.addElement("Open with");
|
||||
model.addElement("Import/Export");
|
||||
categoryList = new JList<>(model);
|
||||
categoryList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
@ -88,7 +88,7 @@ public class SettingsDialog extends JDialog {
|
||||
cards.add(buildBehaviorPanel(), "Behavior");
|
||||
cards.add(buildSortingPanel(), "Sorting");
|
||||
cards.add(buildToolbarPanel(), "Toolbar");
|
||||
cards.add(buildAssociationsPanel(), "Associations");
|
||||
cards.add(buildOpenWithPanel(), "Open with");
|
||||
cards.add(buildImportExportPanel(), "Import/Export");
|
||||
|
||||
categoryList.addListSelectionListener(e -> {
|
||||
@ -181,21 +181,24 @@ public class SettingsDialog extends JDialog {
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
|
||||
// Collect Associations settings
|
||||
JPanel assocHolder = (JPanel) panels.get("Associations");
|
||||
if (assocHolder != null) {
|
||||
// Collect Open with settings
|
||||
JPanel owHolder = (JPanel) panels.get("Open with");
|
||||
if (owHolder != null) {
|
||||
try {
|
||||
DefaultTableModel assocModel = (DefaultTableModel) assocHolder.getClientProperty("tableModel");
|
||||
if (assocModel != null) {
|
||||
java.util.Map<String, String> map = new java.util.HashMap<>();
|
||||
for (int i = 0; i < assocModel.getRowCount(); i++) {
|
||||
String ext = (String) assocModel.getValueAt(i, 0);
|
||||
String cmd = (String) assocModel.getValueAt(i, 1);
|
||||
if (ext != null && !ext.trim().isEmpty() && cmd != null && !cmd.trim().isEmpty()) {
|
||||
map.put(ext.trim(), cmd.trim());
|
||||
DefaultTableModel owModel = (DefaultTableModel) owHolder.getClientProperty("tableModel");
|
||||
if (owModel != null) {
|
||||
java.util.List<cz.kamma.kfmanager.config.AppConfig.OpenWithEntry> list = new java.util.ArrayList<>();
|
||||
for (int i = 0; i < owModel.getRowCount(); i++) {
|
||||
String label = (String) owModel.getValueAt(i, 0);
|
||||
String type = (String) owModel.getValueAt(i, 1);
|
||||
String cmd = (String) owModel.getValueAt(i, 2);
|
||||
if (label != null && !label.trim().isEmpty() &&
|
||||
type != null && !type.trim().isEmpty() &&
|
||||
cmd != null && !cmd.trim().isEmpty()) {
|
||||
list.add(new cz.kamma.kfmanager.config.AppConfig.OpenWithEntry(label.trim(), type.trim(), cmd.trim()));
|
||||
}
|
||||
}
|
||||
config.setFileAssociations(map);
|
||||
config.setOpenWithEntries(list);
|
||||
}
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
@ -231,7 +234,7 @@ public class SettingsDialog extends JDialog {
|
||||
config.setExternalEditorPath(originalExternalEditorPath);
|
||||
config.setToolbarButtonSize(originalToolbarButtonSize);
|
||||
config.setToolbarIconSize(originalToolbarIconSize);
|
||||
config.setFileAssociations(originalFileAssociations);
|
||||
config.setOpenWithEntries(originalOpenWithEntries);
|
||||
|
||||
// Notify UI to revert changes
|
||||
if (onChange != null) onChange.run();
|
||||
@ -632,19 +635,19 @@ public class SettingsDialog extends JDialog {
|
||||
return p;
|
||||
}
|
||||
|
||||
private JPanel buildAssociationsPanel() {
|
||||
private JPanel buildOpenWithPanel() {
|
||||
JPanel p = new JPanel(new BorderLayout(8, 8));
|
||||
p.setBorder(BorderFactory.createEmptyBorder(12, 12, 12, 12));
|
||||
|
||||
JLabel hint = new JLabel("<html>Use <b>wildcards</b> (e.g. *.tar.gz) or plain extension (e.g. txt).<br>" +
|
||||
JLabel hint = new JLabel("<html>Use <b>directory</b> or <b>extension</b> (e.g. txt) for Type.<br>" +
|
||||
"Use <b>%f</b> for full path, <b>%n</b> for filename. " +
|
||||
"If no placeholder is used, path is appended to the end.</html>");
|
||||
p.add(hint, BorderLayout.NORTH);
|
||||
|
||||
DefaultTableModel model = new DefaultTableModel(new String[]{"Pattern", "Command"}, 0);
|
||||
java.util.Map<String, String> current = config.getFileAssociations();
|
||||
for (java.util.Map.Entry<String, String> entry : current.entrySet()) {
|
||||
model.addRow(new Object[]{entry.getKey(), entry.getValue()});
|
||||
DefaultTableModel model = new DefaultTableModel(new String[]{"Label", "Type", "Command"}, 0);
|
||||
java.util.List<cz.kamma.kfmanager.config.AppConfig.OpenWithEntry> current = config.getOpenWithEntries();
|
||||
for (cz.kamma.kfmanager.config.AppConfig.OpenWithEntry entry : current) {
|
||||
model.addRow(new Object[]{entry.label, entry.type, entry.command});
|
||||
}
|
||||
|
||||
JTable table = new JTable(model);
|
||||
@ -652,7 +655,7 @@ public class SettingsDialog extends JDialog {
|
||||
|
||||
JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
JButton addBtn = new JButton("Add");
|
||||
addBtn.addActionListener(e -> model.addRow(new Object[]{"", ""}));
|
||||
addBtn.addActionListener(e -> model.addRow(new Object[]{"", "", ""}));
|
||||
JButton removeBtn = new JButton("Remove");
|
||||
removeBtn.addActionListener(e -> {
|
||||
int row = table.getSelectedRow();
|
||||
@ -665,7 +668,7 @@ public class SettingsDialog extends JDialog {
|
||||
// Save reference for OK action
|
||||
JPanel holder = new JPanel();
|
||||
holder.putClientProperty("tableModel", model);
|
||||
panels.put("Associations", holder);
|
||||
panels.put("Open with", holder);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user