From 21d8454c612aff1754ccddf6628e57bac1558e88 Mon Sep 17 00:00:00 2001 From: Radek Davidek Date: Tue, 20 Jan 2026 16:10:04 +0100 Subject: [PATCH] added folder shortcuts --- .../kamma/kfmanager/ui/ColoredFolderIcon.java | 44 ++++ .../cz/kamma/kfmanager/ui/FilePanelTab.java | 209 +----------------- .../kamma/kfmanager/ui/FileSpecificIcon.java | 133 +++++++++++ .../cz/kamma/kfmanager/ui/MainWindow.java | 36 ++- .../cz/kamma/kfmanager/ui/UpArrowIcon.java | 40 ++++ 5 files changed, 253 insertions(+), 209 deletions(-) create mode 100644 src/main/java/cz/kamma/kfmanager/ui/ColoredFolderIcon.java create mode 100644 src/main/java/cz/kamma/kfmanager/ui/FileSpecificIcon.java create mode 100644 src/main/java/cz/kamma/kfmanager/ui/UpArrowIcon.java diff --git a/src/main/java/cz/kamma/kfmanager/ui/ColoredFolderIcon.java b/src/main/java/cz/kamma/kfmanager/ui/ColoredFolderIcon.java new file mode 100644 index 0000000..605ef4a --- /dev/null +++ b/src/main/java/cz/kamma/kfmanager/ui/ColoredFolderIcon.java @@ -0,0 +1,44 @@ +package cz.kamma.kfmanager.ui; + +import javax.swing.Icon; +import java.awt.*; + +/** + * Custom icon for directories that uses a configurable color. + */ +public class ColoredFolderIcon implements Icon { + private final Color color; + + public ColoredFolderIcon(Color color) { + this.color = color; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + g2.setColor(color); + // Draw a simple folder shape + // Main body + g2.fillRect(x + 2, y + 4, 12, 8); + // Tab + g2.fillRect(x + 2, y + 2, 5, 2); + + // Optional: outline for depth + g2.setColor(color.darker()); + g2.drawRect(x + 2, y + 4, 12, 8); + + g2.dispose(); + } + + @Override + public int getIconWidth() { + return 16; + } + + @Override + public int getIconHeight() { + return 16; + } +} diff --git a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java index 57808d1..7a3a5ab 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java +++ b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java @@ -2118,7 +2118,7 @@ public class FilePanelTab extends JPanel { } } else { // Use type-specific icon for files - icon = new FileSpecificIcon(getFileType(item.getName())); + icon = new FileSpecificIcon(FileSpecificIcon.getFileType(item.getName())); } if (viewMode == ViewMode.BRIEF) { @@ -2779,213 +2779,6 @@ public class FilePanelTab extends JPanel { } } - /** - * Custom icon for directories that uses a configurable color. - */ - private static class ColoredFolderIcon implements Icon { - private final Color color; - - public ColoredFolderIcon(Color color) { - this.color = color; - } - - @Override - public void paintIcon(Component c, Graphics g, int x, int y) { - Graphics2D g2 = (Graphics2D) g.create(); - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - g2.setColor(color); - // Draw a simple folder shape - // Main body - g2.fillRect(x + 2, y + 4, 12, 8); - // Tab - g2.fillRect(x + 2, y + 2, 5, 2); - - // Optional: outline for depth - g2.setColor(color.darker()); - g2.drawRect(x + 2, y + 4, 12, 8); - - g2.dispose(); - } - - @Override - public int getIconWidth() { - return 16; - } - - @Override - public int getIconHeight() { - return 16; - } - } - - /** - * Custom icon for ".." that shows an upward arrow. - */ - private static class UpArrowIcon implements Icon { - private final Color color; - - public UpArrowIcon(Color color) { - this.color = color; - } - - @Override - public void paintIcon(Component c, Graphics g, int x, int y) { - Graphics2D g2 = (Graphics2D) g.create(); - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - g2.setColor(color); - // Draw a simple upward arrow - int[] xPoints = {x + 8, x + 3, x + 13}; - int[] yPoints = {y + 3, y + 10, y + 10}; - g2.fillPolygon(xPoints, yPoints, 3); - g2.fillRect(x + 7, y + 10, 2, 4); - - g2.dispose(); - } - - @Override - public int getIconWidth() { - return 16; - } - - @Override - public int getIconHeight() { - return 16; - } - } - - /** - * Custom icon for files that provides different visuals for different file types. - */ - private static class FileSpecificIcon implements Icon { - public enum Type { IMAGE, VIDEO, AUDIO, ARCHIVE, CODE, TEXT, DOC, EXEC, OTHER } - private final Type type; - private final Color color; - - public FileSpecificIcon(Type type) { - this.type = type; - switch (type) { - case IMAGE: this.color = new Color(70, 130, 180); break; // SteelBlue - case VIDEO: this.color = new Color(205, 92, 92); break; // IndianRed - case AUDIO: this.color = new Color(147, 112, 219); break; // MediumSlateBlue - case ARCHIVE: this.color = new Color(210, 180, 140); break; // Tan - case CODE: this.color = new Color(60, 179, 113); break; // MediumSeaGreen - case TEXT: this.color = Color.GRAY; break; - case DOC: this.color = new Color(70, 90, 180); break; // Darker blue - case EXEC: this.color = new Color(0, 128, 128); break; // Teal - default: this.color = Color.LIGHT_GRAY; break; - } - } - - @Override - public void paintIcon(Component c, Graphics g, int x, int y) { - Graphics2D g2 = (Graphics2D) g.create(); - g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - g2.setColor(color); - // Draw a basic "sheet" shape - g2.fillRect(x + 3, y + 2, 10, 13); - - // Draw a tiny folded corner - g2.setColor(color.darker()); - int[] cx = {x + 10, x + 13, x + 10}; - int[] cy = {y + 2, y + 5, y + 5}; - g2.fillPolygon(cx, cy, 3); - - // Draw a small indicator for the type - g2.setColor(Color.WHITE); - switch (type) { - case IMAGE: - g2.fillOval(x + 5, y + 6, 2, 2); // "Sun" - g2.drawRect(x + 5, y + 9, 4, 3); // Simple mountain - break; - case VIDEO: - int[] vxp = {x + 6, x + 6, x + 10}; - int[] vyp = {y + 5, y + 11, y + 8}; - g2.fillPolygon(vxp, vyp, 3); - break; - case AUDIO: - g2.fillRect(x + 6, y + 5, 3, 2); - g2.fillRect(x + 8, y + 5, 1, 6); - g2.fillOval(x + 5, y + 10, 3, 3); - break; - case ARCHIVE: - g2.drawRect(x + 5, y + 4, 6, 8); - g2.drawLine(x + 5, y + 7, x + 11, y + 7); - g2.drawLine(x + 5, y + 10, x + 11, y + 10); - break; - case CODE: - g2.drawLine(x + 5, y + 9, x + 7, y + 7); // < - g2.drawLine(x + 5, y + 9, x + 7, y + 11); - g2.drawLine(x + 9, y + 7, x + 11, y + 9); // > - g2.drawLine(x + 9, y + 11, x + 11, y + 9); - break; - case TEXT: - case DOC: - g2.drawLine(x + 5, y + 6, x + 11, y + 6); - g2.drawLine(x + 5, y + 9, x + 11, y + 9); - g2.drawLine(x + 5, y + 12, x + 8, y + 12); - break; - case EXEC: - g2.drawOval(x + 5, y + 5, 6, 6); - g2.drawLine(x + 8, y + 4, x + 8, y + 12); - g2.drawLine(x + 4, y + 8, x + 12, y + 8); - break; - default: - break; - } - - g2.dispose(); - } - - @Override - public int getIconWidth() { return 16; } - - @Override - public int getIconHeight() { return 16; } - } - - private FileSpecificIcon.Type getFileType(String fileName) { - String name = fileName.toLowerCase(); - if (name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".png") || - name.endsWith(".gif") || name.endsWith(".bmp") || name.endsWith(".svg") || - name.endsWith(".webp") || name.endsWith(".ico") || name.endsWith(".tif")) return FileSpecificIcon.Type.IMAGE; - - if (name.endsWith(".mp4") || name.endsWith(".mkv") || name.endsWith(".avi") || - name.endsWith(".mov") || name.endsWith(".wmv") || name.endsWith(".flv") || - name.endsWith(".webm") || name.endsWith(".mpg") || name.endsWith(".mpeg")) return FileSpecificIcon.Type.VIDEO; - - if (name.endsWith(".mp3") || name.endsWith(".wav") || name.endsWith(".ogg") || - name.endsWith(".flac") || name.endsWith(".m4a") || name.endsWith(".aac") || - name.endsWith(".wma")) return FileSpecificIcon.Type.AUDIO; - - if (name.endsWith(".zip") || name.endsWith(".rar") || name.endsWith(".7z") || - name.endsWith(".tar") || name.endsWith(".gz") || name.endsWith(".bz2") || - name.endsWith(".xz") || name.endsWith(".jar") || name.endsWith(".deb") || - name.endsWith(".rpm")) return FileSpecificIcon.Type.ARCHIVE; - - if (name.endsWith(".java") || name.endsWith(".c") || name.endsWith(".cpp") || - name.endsWith(".h") || name.endsWith(".py") || name.endsWith(".js") || - name.endsWith(".html") || name.endsWith(".css") || name.endsWith(".ts") || - name.endsWith(".xml") || name.endsWith(".json") || name.endsWith(".sh") || - name.endsWith(".php") || name.endsWith(".kt") || name.endsWith(".cs") || - name.endsWith(".go") || name.endsWith(".rb") || name.endsWith(".sql")) return FileSpecificIcon.Type.CODE; - - if (name.endsWith(".pdf") || name.endsWith(".doc") || name.endsWith(".docx") || - name.endsWith(".odt") || name.endsWith(".xls") || name.endsWith(".xlsx") || - name.endsWith(".ppt") || name.endsWith(".pptx") || name.endsWith(".rtf")) return FileSpecificIcon.Type.DOC; - - if (name.endsWith(".txt") || name.endsWith(".md") || name.endsWith(".log") || - name.endsWith(".conf") || name.endsWith(".ini") || name.endsWith(".csv") || - name.endsWith(".properties")) return FileSpecificIcon.Type.TEXT; - - if (name.endsWith(".exe") || name.endsWith(".bin") || name.endsWith(".com") || - name.endsWith(".bat") || name.endsWith(".msi") || name.endsWith(".appimage")) return FileSpecificIcon.Type.EXEC; - - return FileSpecificIcon.Type.OTHER; - } - private void showAssociationDialog(File file) { String name = file.getName(); String ext = ""; diff --git a/src/main/java/cz/kamma/kfmanager/ui/FileSpecificIcon.java b/src/main/java/cz/kamma/kfmanager/ui/FileSpecificIcon.java new file mode 100644 index 0000000..0e2274a --- /dev/null +++ b/src/main/java/cz/kamma/kfmanager/ui/FileSpecificIcon.java @@ -0,0 +1,133 @@ +package cz.kamma.kfmanager.ui; + +import javax.swing.Icon; +import java.awt.*; + +/** + * Custom icon for files with type-specific indicators. + */ +public class FileSpecificIcon implements Icon { + public enum Type { IMAGE, VIDEO, AUDIO, ARCHIVE, CODE, TEXT, DOC, EXEC, OTHER } + private final Type type; + private final Color color; + + public FileSpecificIcon(Type type) { + this.type = type; + switch (type) { + case IMAGE: this.color = new Color(70, 130, 180); break; // SteelBlue + case VIDEO: this.color = new Color(205, 92, 92); break; // IndianRed + case AUDIO: this.color = new Color(147, 112, 219); break; // MediumSlateBlue + case ARCHIVE: this.color = new Color(210, 180, 140); break; // Tan + case CODE: this.color = new Color(60, 179, 113); break; // MediumSeaGreen + case TEXT: this.color = Color.GRAY; break; + case DOC: this.color = new Color(70, 90, 180); break; // Darker blue + case EXEC: this.color = new Color(0, 128, 128); break; // Teal + default: this.color = Color.LIGHT_GRAY; break; + } + } + + public static Type getFileType(String fileName) { + String name = fileName.toLowerCase(); + if (name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".png") || + name.endsWith(".gif") || name.endsWith(".bmp") || name.endsWith(".svg") || + name.endsWith(".webp") || name.endsWith(".ico") || name.endsWith(".tif")) return Type.IMAGE; + + if (name.endsWith(".mp4") || name.endsWith(".mkv") || name.endsWith(".avi") || + name.endsWith(".mov") || name.endsWith(".wmv") || name.endsWith(".flv") || + name.endsWith(".webm") || name.endsWith(".mpg") || name.endsWith(".mpeg")) return Type.VIDEO; + + if (name.endsWith(".mp3") || name.endsWith(".wav") || name.endsWith(".ogg") || + name.endsWith(".flac") || name.endsWith(".m4a") || name.endsWith(".aac") || + name.endsWith(".wma")) return Type.AUDIO; + + if (name.endsWith(".zip") || name.endsWith(".rar") || name.endsWith(".7z") || + name.endsWith(".tar") || name.endsWith(".gz") || name.endsWith(".bz2") || + name.endsWith(".xz") || name.endsWith(".jar") || name.endsWith(".deb") || + name.endsWith(".rpm") || name.endsWith(".iso")) return Type.ARCHIVE; + + if (name.endsWith(".java") || name.endsWith(".c") || name.endsWith(".cpp") || + name.endsWith(".cs") || name.endsWith(".py") || name.endsWith(".js") || + name.endsWith(".ts") || name.endsWith(".html") || name.endsWith(".css") || + name.endsWith(".xml") || name.endsWith(".json") || name.endsWith(".sh") || + name.endsWith(".bat") || name.endsWith(".pl") || name.endsWith(".php")) return Type.CODE; + + if (name.endsWith(".pdf") || name.endsWith(".doc") || name.endsWith(".docx") || + name.endsWith(".xls") || name.endsWith(".xlsx") || name.endsWith(".ppt") || + name.endsWith(".pptx") || name.endsWith(".odt") || name.endsWith(".ods")) return Type.DOC; + + if (name.endsWith(".exe") || name.endsWith(".msi") || name.endsWith(".bin") || + name.endsWith(".run") || name.endsWith(".app")) return Type.EXEC; + + if (name.endsWith(".txt") || name.endsWith(".log") || name.endsWith(".md") || + name.endsWith(".cfg") || name.endsWith(".ini") || name.endsWith(".properties")) return Type.TEXT; + + return Type.OTHER; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + g2.setColor(color); + // Draw a basic "sheet" shape + g2.fillRect(x + 3, y + 2, 10, 13); + + // Draw a tiny folded corner + g2.setColor(color.darker()); + int[] cx = {x + 10, x + 13, x + 10}; + int[] cy = {y + 2, y + 5, y + 5}; + g2.fillPolygon(cx, cy, 3); + + // Draw a small indicator for the type + g2.setColor(Color.WHITE); + switch (type) { + case IMAGE: + g2.fillOval(x + 5, y + 6, 2, 2); // "Sun" + g2.drawRect(x + 5, y + 9, 4, 3); // Simple mountain + break; + case VIDEO: + int[] vxp = {x + 6, x + 6, x + 10}; + int[] vyp = {y + 5, y + 11, y + 8}; + g2.fillPolygon(vxp, vyp, 3); + break; + case AUDIO: + g2.fillRect(x + 6, y + 5, 3, 2); + g2.fillRect(x + 8, y + 5, 1, 6); + g2.fillOval(x + 5, y + 10, 3, 3); + break; + case ARCHIVE: + g2.drawRect(x + 5, y + 4, 6, 8); + g2.drawLine(x + 5, y + 7, x + 11, y + 7); + g2.drawLine(x + 5, y + 10, x + 11, y + 10); + break; + case CODE: + g2.drawLine(x + 5, y + 9, x + 7, y + 7); // < + g2.drawLine(x + 5, y + 9, x + 7, y + 11); + g2.drawLine(x + 9, y + 7, x + 11, y + 9); // > + g2.drawLine(x + 9, y + 11, x + 11, y + 9); + break; + case TEXT: + case DOC: + g2.drawLine(x + 5, y + 6, x + 11, y + 6); + g2.drawLine(x + 5, y + 9, x + 11, y + 9); + g2.drawLine(x + 5, y + 12, x + 8, y + 12); + break; + case EXEC: + g2.drawOval(x + 5, y + 5, 6, 6); + g2.drawLine(x + 8, y + 4, x + 8, y + 12); + g2.drawLine(x + 4, y + 8, x + 12, y + 8); + break; + default: + break; + } + + g2.dispose(); + } + + @Override + public int getIconWidth() { return 16; } + + @Override + public int getIconHeight() { return 16; } +} diff --git a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java index 3ae8b3c..7923fea 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java +++ b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java @@ -470,6 +470,30 @@ public class MainWindow extends JFrame { } catch (Exception ignore) {} } + // If no custom icon, try to use the same icons as in file panels + if (!hasIcon) { + try { + File target = new File(s.command); + if (target.exists()) { + Icon customIcon; + if (target.isDirectory()) { + customIcon = new ColoredFolderIcon(config.getFolderColor()); + } else { + customIcon = new FileSpecificIcon(FileSpecificIcon.getFileType(target.getName())); + } + + // Scale custom icon to toolbar size + java.awt.image.BufferedImage img = new java.awt.image.BufferedImage( + customIcon.getIconWidth(), customIcon.getIconHeight(), java.awt.image.BufferedImage.TYPE_INT_ARGB); + java.awt.Graphics g = img.getGraphics(); + customIcon.paintIcon(null, g, 0, 0); + g.dispose(); + btn.setIcon(new ImageIcon(img.getScaledInstance(iconSize, iconSize, Image.SCALE_SMOOTH))); + hasIcon = true; + } + } catch (Exception ignore) {} + } + // If no icon found, use the label text, otherwise use label as tooltip if (!hasIcon) { btn.setText(s.label); @@ -477,7 +501,17 @@ public class MainWindow extends JFrame { btn.setToolTipText(s.label + " (" + s.command + ")"); btn.setFocusable(false); - btn.addActionListener(e -> executeNative(s.command, s.workingDir)); + btn.addActionListener(e -> { + // If command is a directory that exists, change active panel directory + File dir = new File(s.command); + if (dir.exists() && dir.isDirectory()) { + if (activePanel != null) { + activePanel.loadDirectory(dir); + } + } else { + executeNative(s.command, s.workingDir); + } + }); // Context menu for Edit/Delete JPopupMenu shortcutPopup = new JPopupMenu(); diff --git a/src/main/java/cz/kamma/kfmanager/ui/UpArrowIcon.java b/src/main/java/cz/kamma/kfmanager/ui/UpArrowIcon.java new file mode 100644 index 0000000..eac5919 --- /dev/null +++ b/src/main/java/cz/kamma/kfmanager/ui/UpArrowIcon.java @@ -0,0 +1,40 @@ +package cz.kamma.kfmanager.ui; + +import javax.swing.Icon; +import java.awt.*; + +/** + * Custom icon for ".." that shows an upward arrow. + */ +public class UpArrowIcon implements Icon { + private final Color color; + + public UpArrowIcon(Color color) { + this.color = color; + } + + @Override + public void paintIcon(Component c, Graphics g, int x, int y) { + Graphics2D g2 = (Graphics2D) g.create(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + g2.setColor(color); + // Draw a simple upward arrow + int[] xPoints = {x + 8, x + 3, x + 13}; + int[] yPoints = {y + 3, y + 10, y + 10}; + g2.fillPolygon(xPoints, yPoints, 3); + g2.fillRect(x + 7, y + 10, 2, 4); + + g2.dispose(); + } + + @Override + public int getIconWidth() { + return 16; + } + + @Override + public int getIconHeight() { + return 16; + } +}