diff --git a/pom.xml b/pom.xml
index 3d719c0..e137126 100644
--- a/pom.xml
+++ b/pom.xml
@@ -97,5 +97,15 @@
junrar
7.4.1
+
+ com.formdev
+ flatlaf
+ 3.5.1
+
+
+ com.formdev
+ flatlaf-extras
+ 3.5.1
+
diff --git a/src/main/java/cz/kamma/kfmanager/MainApp.java b/src/main/java/cz/kamma/kfmanager/MainApp.java
index aa9ce4c..1a51a98 100644
--- a/src/main/java/cz/kamma/kfmanager/MainApp.java
+++ b/src/main/java/cz/kamma/kfmanager/MainApp.java
@@ -1,10 +1,14 @@
package cz.kamma.kfmanager;
import cz.kamma.kfmanager.ui.MainWindow;
+import com.formdev.flatlaf.FlatDarkLaf;
+import com.formdev.flatlaf.FlatLightLaf;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
/**
* Main application class for KF File Manager
@@ -17,11 +21,26 @@ public class MainApp {
// Set application name for X11/Wayland WM_CLASS
System.setProperty("awt.app.name", "cz-kamma-kfmanager-MainApp");
- // Set look and feel to system default
+ // Use FlatLaf for modern look and better system theme support
try {
- UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ if (System.getProperty("os.name").toLowerCase().contains("win")) {
+ // On Windows, use FlatLaf and detect system theme (light/dark)
+ if (isWindowsDarkMode()) {
+ FlatDarkLaf.setup();
+ } else {
+ FlatLightLaf.setup();
+ }
+ } else {
+ // On Linux and macOS, use FlatLaf as a better-looking alternative
+ // to the default system L&F
+ FlatLightLaf.setup();
+ }
} catch (Exception e) {
- e.printStackTrace();
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
}
// Enable arrow key navigation in JOptionPane dialogs
@@ -34,6 +53,22 @@ public class MainApp {
});
}
+ private static boolean isWindowsDarkMode() {
+ try {
+ Process process = Runtime.getRuntime().exec("reg query \"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize\" /v AppsUseLightTheme");
+ BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ if (line.contains("AppsUseLightTheme")) {
+ return line.contains("0x0");
+ }
+ }
+ } catch (Exception e) {
+ // ignore and fallback to light
+ }
+ return false;
+ }
+
private static void setupGlobalKeyNavigation() {
Toolkit.getDefaultToolkit().addAWTEventListener(event -> {
if (event instanceof KeyEvent) {
diff --git a/src/main/java/cz/kamma/kfmanager/ui/FilePanel.java b/src/main/java/cz/kamma/kfmanager/ui/FilePanel.java
index a584c25..42661ae 100644
--- a/src/main/java/cz/kamma/kfmanager/ui/FilePanel.java
+++ b/src/main/java/cz/kamma/kfmanager/ui/FilePanel.java
@@ -774,6 +774,13 @@ public class FilePanel extends JPanel {
}
}
+ public void navigateUp() {
+ FilePanelTab tab = getCurrentTab();
+ if (tab != null) {
+ tab.navigateUp();
+ }
+ }
+
public void showArchiveFile(File archive, String entryName) {
FilePanelTab tab = getCurrentTab();
if (tab != null) {
diff --git a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java
index fbc8803..95bb889 100644
--- a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java
+++ b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java
@@ -63,7 +63,7 @@ public class FilePanelTab extends JPanel {
private Runnable onDirectoryChanged;
private Runnable onSwitchPanelRequested;
// Appearance customization
- private Color selectionColor = new Color(184, 207, 229);
+ private Color selectionColor = null;
private Color markedColor = new Color(204, 153, 0);
// Sorting state for FULL mode header clicks
private int sortColumn = -1; // 0=name,1=size,2=date
@@ -1462,7 +1462,21 @@ public class FilePanelTab extends JPanel {
cmdList.add(fullPath);
}
- new ProcessBuilder(cmdList).directory(file.getParentFile()).start();
+ try {
+ new ProcessBuilder(cmdList).directory(file.getParentFile()).start();
+ } catch (IOException ex) {
+ String osName = System.getProperty("os.name").toLowerCase();
+ if (osName.contains("win")) {
+ // Try via cmd.exe for Windows shell commands/scripts
+ new ProcessBuilder("cmd", "/c", trimmedCmd + (hasPlaceholder ? "" : " \"" + fullPath + "\""))
+ .directory(file.getParentFile()).start();
+ } else if (osName.contains("linux") || osName.contains("mac")) {
+ new ProcessBuilder("sh", "-c", trimmedCmd + (hasPlaceholder ? "" : " '" + fullPath + "'"))
+ .directory(file.getParentFile()).start();
+ } else {
+ throw ex;
+ }
+ }
} catch (Exception ex) {
try { JOptionPane.showMessageDialog(this, "Cannot execute command: " + ex.getMessage()); } catch (Exception ignore) {}
}
@@ -2368,6 +2382,15 @@ public class FilePanelTab extends JPanel {
// Hide table header in BRIEF mode to save vertical space and match requirements
if (fileTable.getTableHeader() != null) {
fileTable.getTableHeader().setVisible(mode != ViewMode.BRIEF);
+ // In JScrollPane, manually managing columnHeader might be necessary to reclaim space
+ Component parent = fileTable.getParent();
+ if (parent instanceof JViewport) {
+ Component scroll = parent.getParent();
+ if (scroll instanceof JScrollPane) {
+ JScrollPane sp = (JScrollPane) scroll;
+ sp.setColumnHeaderView(mode == ViewMode.BRIEF ? null : fileTable.getTableHeader());
+ }
+ }
}
briefCurrentColumn = 0;
updateColumnRenderers();
@@ -2557,7 +2580,7 @@ public class FilePanelTab extends JPanel {
// Show selection highlight even when the table doesn't have focus, but only
// if this panel/tab is the active one.
if (isCurrentCell && FilePanelTab.this.active) {
- setBackground(selectionColor);
+ setBackground(selectionColor != null ? selectionColor : table.getSelectionBackground());
} else {
setBackground(FilePanelTab.this.getBackground());
}
@@ -2581,7 +2604,11 @@ public class FilePanelTab extends JPanel {
setFont(baseFont.deriveFont(baseStyle));
// Automatically adjust foreground contrast
if (isCurrentCell && FilePanelTab.this.active) {
- setForeground(isDark(selectionColor) ? Color.WHITE : Color.BLACK);
+ if (selectionColor != null) {
+ setForeground(isDark(selectionColor) ? Color.WHITE : Color.BLACK);
+ } else {
+ setForeground(table.getSelectionForeground());
+ }
} else {
setForeground(isDark(FilePanelTab.this.getBackground()) ? Color.WHITE : Color.BLACK);
}
diff --git a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java
index 338a35c..a9649c1 100644
--- a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java
+++ b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java
@@ -2138,8 +2138,21 @@ public class MainWindow extends JFrame {
ProcessBuilder pb = null;
if (osName.contains("win")) {
- // Windows
- pb = new ProcessBuilder("cmd.exe", "/c", "start", "cmd.exe");
+ // Windows - try Windows Terminal first, then PowerShell, then cmd
+ try {
+ // Try to use Windows Terminal if available
+ new ProcessBuilder("wt.exe", "-d", currentDir.getAbsolutePath()).start();
+ return;
+ } catch (Exception e1) {
+ try {
+ // Fallback to PowerShell
+ new ProcessBuilder("powershell.exe").directory(currentDir).start();
+ return;
+ } catch (Exception e2) {
+ // Final fallback to cmd
+ pb = new ProcessBuilder("cmd.exe", "/c", "start", "cmd.exe");
+ }
+ }
} else if (osName.contains("mac")) {
// macOS
pb = new ProcessBuilder("open", "-a", "Terminal", currentDir.getAbsolutePath());
@@ -2191,15 +2204,46 @@ public class MainWindow extends JFrame {
return;
}
+ String trimmed = command.trim();
// Add to history
- addCommandToHistory(command.trim());
+ addCommandToHistory(trimmed);
+ // Handle internal commands like 'cd'
+ if (trimmed.toLowerCase().startsWith("cd ") || trimmed.toLowerCase().equals("cd..") || trimmed.equals("..")) {
+ String targetPath = null;
+ if (trimmed.equals("..") || trimmed.toLowerCase().equals("cd..")) {
+ targetPath = "..";
+ } else {
+ targetPath = trimmed.substring(3).trim();
+ if (targetPath.startsWith("\"") && targetPath.endsWith("\"")) {
+ targetPath = targetPath.substring(1, targetPath.length() - 1);
+ }
+ }
+
+ if (activePanel != null) {
+ if ("..".equals(targetPath)) {
+ activePanel.navigateUp();
+ } else {
+ File targetDir = new File(activePanel.getCurrentDirectory(), targetPath);
+ if (targetDir.exists() && targetDir.isDirectory()) {
+ activePanel.loadDirectory(targetDir);
+ } else {
+ // try absolute path
+ targetDir = new File(targetPath);
+ if (targetDir.exists() && targetDir.isDirectory()) {
+ activePanel.loadDirectory(targetDir);
+ }
+ }
+ }
+ }
+ } else {
+ // Execute natively for other commands
+ executeNative(trimmed, null);
+ }
+
// 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) {
@@ -2224,7 +2268,20 @@ public class MainWindow extends JFrame {
currentDir = new File(System.getProperty("user.home"));
}
+ String osName = System.getProperty("os.name").toLowerCase();
try {
+ String trimmedCmd = command.trim();
+
+ // Special case for Windows terminal commands - launch in a new window
+ if (osName.contains("win")) {
+ String cmdLower = trimmedCmd.toLowerCase();
+ if (cmdLower.equals("cmd") || cmdLower.equals("powershell") ||
+ cmdLower.equals("pwsh") || cmdLower.equals("wt")) {
+ new ProcessBuilder("cmd", "/c", "start", cmdLower).directory(currentDir).start();
+ return;
+ }
+ }
+
// Check if it's a file path that exists (and not a complex command)
if (!command.contains(" ") || (command.startsWith("\"") && command.endsWith("\"") && !command.substring(1, command.length()-1).contains("\""))) {
String path = command.startsWith("\"") ? command.substring(1, command.length()-1) : command;
@@ -2247,10 +2304,11 @@ public class MainWindow extends JFrame {
try {
new ProcessBuilder(cmdList).directory(currentDir).start();
} catch (IOException ex) {
- // Fallback for Linux/macOS: try via shell
- String osName = System.getProperty("os.name").toLowerCase();
+ // Fallback for different OS: try via shell
if (osName.contains("linux") || osName.contains("mac")) {
new ProcessBuilder("sh", "-c", command).directory(currentDir).start();
+ } else if (osName.contains("win")) {
+ new ProcessBuilder("cmd", "/c", command).directory(currentDir).start();
} else {
throw ex;
}