From a4afc103ad99a3306fc213a6aeb7b27109414fd1 Mon Sep 17 00:00:00 2001 From: rdavidek Date: Wed, 21 Jan 2026 23:23:59 +0100 Subject: [PATCH] new system for associations --- .../cz/kamma/kfmanager/config/AppConfig.java | 79 ++++++++------ .../cz/kamma/kfmanager/ui/FilePanelTab.java | 100 +++--------------- .../cz/kamma/kfmanager/ui/SettingsDialog.java | 53 +++++----- 3 files changed, 88 insertions(+), 144 deletions(-) diff --git a/src/main/java/cz/kamma/kfmanager/config/AppConfig.java b/src/main/java/cz/kamma/kfmanager/config/AppConfig.java index dbc25e9..f11a865 100644 --- a/src/main/java/cz/kamma/kfmanager/config/AppConfig.java +++ b/src/main/java/cz/kamma/kfmanager/config/AppConfig.java @@ -367,42 +367,57 @@ public class AppConfig { } } - // --- File Associations --- - /** Returns a map of pattern (extension or wildcard) -> command */ - public java.util.Map getFileAssociations() { - java.util.Map 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 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 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 getOpenWithEntries() { + java.util.List 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 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 --- diff --git a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java index 5c6644d..609e17c 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java +++ b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java @@ -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 associations = persistedConfig.getFileAssociations(); - String name = file.getName(); + java.util.List 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 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; - } - - 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; - } - } + 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 } - 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 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/cz/kamma/kfmanager/ui/SettingsDialog.java b/src/main/java/cz/kamma/kfmanager/ui/SettingsDialog.java index 84103be..6ab4fbf 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/SettingsDialog.java +++ b/src/main/java/cz/kamma/kfmanager/ui/SettingsDialog.java @@ -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 originalFileAssociations; + private final java.util.List 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 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 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("Use wildcards (e.g. *.tar.gz) or plain extension (e.g. txt).
" + + JLabel hint = new JLabel("Use directory or extension (e.g. txt) for Type.
" + "Use %f for full path, %n for filename. " + "If no placeholder is used, path is appended to the end."); p.add(hint, BorderLayout.NORTH); - DefaultTableModel model = new DefaultTableModel(new String[]{"Pattern", "Command"}, 0); - java.util.Map current = config.getFileAssociations(); - for (java.util.Map.Entry entry : current.entrySet()) { - model.addRow(new Object[]{entry.getKey(), entry.getValue()}); + DefaultTableModel model = new DefaultTableModel(new String[]{"Label", "Type", "Command"}, 0); + java.util.List 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; }