command history, translate to english

This commit is contained in:
Radek Davidek 2026-01-16 15:57:26 +01:00
parent 61fb75854e
commit 2c32493e73
7 changed files with 190 additions and 101 deletions

View File

@ -5,21 +5,21 @@ import com.kfmanager.ui.MainWindow;
import javax.swing.*; import javax.swing.*;
/** /**
* Hlavní třída aplikace KF File Manager * Main application class for KF File Manager
*/ */
public class MainApp { public class MainApp {
public static final String APP_VERSION = "0.0.2"; public static final String APP_VERSION = "0.0.2";
public static void main(String[] args) { public static void main(String[] args) {
// Nastavení look and feel podle systému // Set look and feel to system default
try { try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
// Spuštění GUI v Event Dispatch Thread // Start GUI in Event Dispatch Thread
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
MainWindow mainWindow = new MainWindow(); MainWindow mainWindow = new MainWindow();
mainWindow.setVisible(true); mainWindow.setVisible(true);

View File

@ -28,7 +28,7 @@ public class AppConfig {
try (FileInputStream fis = new FileInputStream(configFile)) { try (FileInputStream fis = new FileInputStream(configFile)) {
properties.load(fis); properties.load(fis);
} catch (IOException e) { } catch (IOException e) {
System.err.println("Nepodařilo se načíst konfiguraci: " + e.getMessage()); System.err.println("Could not load configuration: " + e.getMessage());
} }
} }
} }
@ -48,7 +48,7 @@ public class AppConfig {
try (FileOutputStream fos = new FileOutputStream(configFile)) { try (FileOutputStream fos = new FileOutputStream(configFile)) {
properties.store(fos, "KF File Manager Configuration"); properties.store(fos, "KF File Manager Configuration");
} catch (IOException e) { } catch (IOException e) {
System.err.println("Nepodařilo se uložit konfiguraci: " + e.getMessage()); System.err.println("Could not save configuration: " + e.getMessage());
} }
} }
@ -110,7 +110,7 @@ public class AppConfig {
properties.setProperty("rightPanel.path", path); properties.setProperty("rightPanel.path", path);
} }
// ViewMode konfigurace // ViewMode configuration
public String getLeftPanelViewMode() { public String getLeftPanelViewMode() {
return properties.getProperty("leftPanel.viewMode", "FULL"); return properties.getProperty("leftPanel.viewMode", "FULL");
} }
@ -178,7 +178,7 @@ public class AppConfig {
properties.setProperty("rightPanel.selectedIndex", String.valueOf(selectedIndex)); properties.setProperty("rightPanel.selectedIndex", String.valueOf(selectedIndex));
} }
// Font konfigurace // Font configuration
public String getEditorFontName() { public String getEditorFontName() {
return properties.getProperty("editor.font.name", "Monospaced"); return properties.getProperty("editor.font.name", "Monospaced");
} }
@ -533,4 +533,32 @@ public class AppConfig {
properties.remove("search.content.history." + i); properties.remove("search.content.history." + i);
} }
} }
// --- Command line history persistence ---
public java.util.List<String> getCommandLineHistory() {
java.util.List<String> list = new java.util.ArrayList<>();
int count = Integer.parseInt(properties.getProperty("cmd.history.count", "0"));
for (int i = 0; i < count; i++) {
String v = properties.getProperty("cmd.history." + i, null);
if (v != null && !v.isEmpty()) list.add(v);
}
return list;
}
public void saveCommandLineHistory(java.util.List<String> history) {
if (history == null) {
properties.setProperty("cmd.history.count", "0");
return;
}
int limit = Math.min(history.size(), 50);
properties.setProperty("cmd.history.count", String.valueOf(limit));
for (int i = 0; i < limit; i++) {
properties.setProperty("cmd.history." + i, history.get(i));
}
// remove old entries beyond limit
int old = Integer.parseInt(properties.getProperty("cmd.history.count", "0"));
for (int i = limit; i < old; i++) {
properties.remove("cmd.history." + i);
}
}
} }

View File

@ -27,7 +27,7 @@ public class FileItem {
this.isDirectory = file.isDirectory(); this.isDirectory = file.isDirectory();
this.marked = false; this.marked = false;
// Načíst ikonu ze systému // Load icon from system
this.icon = FileSystemView.getFileSystemView().getSystemIcon(file); this.icon = FileSystemView.getFileSystemView().getSystemIcon(file);
} }

View File

@ -367,7 +367,7 @@ public class FileOperations {
// legacy matchesPattern removed filename wildcard handling is done via a precompiled Pattern // legacy matchesPattern removed filename wildcard handling is done via a precompiled Pattern
/** /**
* Callback pro progress operací * Callback for operation progress
*/ */
public interface ProgressCallback { public interface ProgressCallback {
void onProgress(long current, long total, String currentFile); void onProgress(long current, long total, String currentFile);
@ -375,7 +375,7 @@ public class FileOperations {
} }
/** /**
* Callback pro vyhledávání * Callback for search
*/ */
public interface SearchCallback { public interface SearchCallback {
void onFileFound(File file); void onFileFound(File file);

View File

@ -144,11 +144,11 @@ public class FilePanel extends JPanel {
add(topPanel, BorderLayout.NORTH); add(topPanel, BorderLayout.NORTH);
// JTabbedPane pro taby // JTabbedPane for tabs
tabbedPane = new JTabbedPane(); tabbedPane = new JTabbedPane();
tabbedPane.setTabPlacement(JTabbedPane.TOP); tabbedPane.setTabPlacement(JTabbedPane.TOP);
// Listener pro aktualizaci cesty a stylu při změně tabu // Listener for updating path and style on tab change
tabbedPane.addChangeListener(e -> { tabbedPane.addChangeListener(e -> {
updatePathField(); updatePathField();
updateTabStyles(); updateTabStyles();
@ -174,19 +174,19 @@ public class FilePanel extends JPanel {
* Add a new tab with a directory * Add a new tab with a directory
*/ */
public void addNewTab(String path) { public void addNewTab(String path) {
// Získat view mode z aktuálního tabu // Get view mode from current tab
ViewMode currentMode = getViewMode(); ViewMode currentMode = getViewMode();
FilePanelTab tab = new FilePanelTab(path); FilePanelTab tab = new FilePanelTab(path);
if (appConfig != null) tab.setAppConfig(appConfig); if (appConfig != null) tab.setAppConfig(appConfig);
// Nastavit callback pro aktualizaci názvu tabu při změně adresáře // Set callback for updating tab title on directory change
tab.setOnDirectoryChanged(() -> updateTabTitle(tab)); tab.setOnDirectoryChanged(() -> updateTabTitle(tab));
// Forward switchPanel callback to the tab so TAB works from any tab // Forward switchPanel callback to the tab so TAB works from any tab
tab.setOnSwitchPanelRequested(switchPanelCallback); tab.setOnSwitchPanelRequested(switchPanelCallback);
// Nastavit stejný view mode jako aktuální tab // Set same view mode as current tab
if (currentMode != null) { if (currentMode != null) {
tab.setViewMode(currentMode); tab.setViewMode(currentMode);
} }
@ -196,11 +196,11 @@ public class FilePanel extends JPanel {
tabbedPane.addTab(tabTitle, tab); tabbedPane.addTab(tabTitle, tab);
tabbedPane.setSelectedComponent(tab); tabbedPane.setSelectedComponent(tab);
// Aktualizovat path field // Update path field
updatePathField(); updatePathField();
updateTabStyles(); updateTabStyles();
// Nastavit focus na tabulku v novém tabu // Set focus to the table in the new tab
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
tab.getFileTable().requestFocusInWindow(); tab.getFileTable().requestFocusInWindow();
// Ensure renderers are attached now that the tab is added to the UI // Ensure renderers are attached now that the tab is added to the UI
@ -209,7 +209,7 @@ public class FilePanel extends JPanel {
} }
/** /**
* Přidá nový tab a explicitně nastaví ViewMode pro tento tab. * Add a new tab and explicitly set the ViewMode for this tab.
*/ */
public void addNewTabWithMode(String path, ViewMode mode) { public void addNewTabWithMode(String path, ViewMode mode) {
FilePanelTab tab = new FilePanelTab(path); FilePanelTab tab = new FilePanelTab(path);
@ -278,7 +278,7 @@ public class FilePanel extends JPanel {
} }
/** /**
* Obnoví sadu tabů podle zadaných cest a view módů. Pokud je seznam prázdný, nic se nestane. * Restore the set of tabs according to specified paths and view modes. If the list is empty, nothing happens.
*/ */
public void restoreTabs(java.util.List<String> paths, java.util.List<String> viewModes, int selectedIndex) { public void restoreTabs(java.util.List<String> paths, java.util.List<String> viewModes, int selectedIndex) {
if (paths == null || paths.isEmpty()) return; if (paths == null || paths.isEmpty()) return;
@ -338,7 +338,7 @@ public class FilePanel extends JPanel {
private String getTabTitle(String path) { private String getTabTitle(String path) {
String tabTitle = new File(path).getName(); String tabTitle = new File(path).getName();
if (tabTitle.isEmpty()) { if (tabTitle.isEmpty()) {
tabTitle = path; // Pro root cesty jako "C:\" tabTitle = path; // For root paths like "C:\"
} }
return tabTitle; return tabTitle;
} }
@ -351,7 +351,7 @@ public class FilePanel extends JPanel {
} }
/** /**
* Aktualizuje styl tabů - zvýrazní aktivní tab tučným písmem a barvou. * Updates tab style - highlights the active tab with bold font and color.
*/ */
private void updateTabStyles() { private void updateTabStyles() {
if (tabbedPane == null) return; if (tabbedPane == null) return;
@ -364,11 +364,11 @@ public class FilePanel extends JPanel {
String title = getTabTitle(dir != null ? dir.getAbsolutePath() : ""); String title = getTabTitle(dir != null ? dir.getAbsolutePath() : "");
if (i == selectedIndex) { if (i == selectedIndex) {
// Aktivní tab: tučné písmo a tmavě modrá barva // Active tab: bold font and dark blue color
tabbedPane.setTitleAt(i, "<html><b>" + title + "</b></html>"); tabbedPane.setTitleAt(i, "<html><b>" + title + "</b></html>");
tabbedPane.setForegroundAt(i, new Color(0, 51, 153)); tabbedPane.setForegroundAt(i, new Color(0, 51, 153));
} else { } else {
// Neaktivní tab: normální písmo a výchozí barva // Inactive tab: normal font and default color
tabbedPane.setTitleAt(i, title); tabbedPane.setTitleAt(i, title);
tabbedPane.setForegroundAt(i, null); tabbedPane.setForegroundAt(i, null);
} }
@ -479,7 +479,7 @@ public class FilePanel extends JPanel {
} }
} }
// Delegování metod na aktuální tab // Delegation of methods to current tab
/** /**
* Switch to the next tab in this panel. * Switch to the next tab in this panel.

View File

@ -355,16 +355,16 @@ public class FilePanelTab extends JPanel {
// remain visible. // remain visible.
fileTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); fileTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
// Nastavit Cell Selection mode pouze v BRIEF režimu // Set Cell Selection mode only in BRIEF mode
fileTable.setCellSelectionEnabled(false); fileTable.setCellSelectionEnabled(false);
fileTable.setRowSelectionAllowed(true); fileTable.setRowSelectionAllowed(true);
fileTable.setColumnSelectionAllowed(false); fileTable.setColumnSelectionAllowed(false);
// Odstranit bordery z tabulky // Remove table borders
fileTable.setShowGrid(false); fileTable.setShowGrid(false);
fileTable.setIntercellSpacing(new java.awt.Dimension(0, 0)); fileTable.setIntercellSpacing(new java.awt.Dimension(0, 0));
// Nastavit pozadí tabulky stejné jako pozadí panelu // Set table background same as panel background
fileTable.setBackground(this.getBackground()); fileTable.setBackground(this.getBackground());
fileTable.setOpaque(true); fileTable.setOpaque(true);
@ -383,9 +383,9 @@ public class FilePanelTab extends JPanel {
@Override @Override
public void componentResized(java.awt.event.ComponentEvent e) { public void componentResized(java.awt.event.ComponentEvent e) {
if (viewMode == ViewMode.BRIEF) { if (viewMode == ViewMode.BRIEF) {
// Při změně velikosti v BRIEF módu přepočítat layout // Recalculate layout on resize in BRIEF mode
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
// Zapamatovat si vybranou položku // Remember selected item
String selectedItemName = null; String selectedItemName = null;
int selectedRow = fileTable.getSelectedRow(); int selectedRow = fileTable.getSelectedRow();
if (selectedRow >= 0) { if (selectedRow >= 0) {
@ -395,13 +395,13 @@ public class FilePanelTab extends JPanel {
} }
} }
// Přepočítat layout // Recalculate layout
tableModel.calculateBriefLayout(); tableModel.calculateBriefLayout();
tableModel.fireTableStructureChanged(); tableModel.fireTableStructureChanged();
updateColumnRenderers(); updateColumnRenderers();
updateColumnWidths(); updateColumnWidths();
// Znovu vybrat položku // Re-select item
if (selectedItemName != null) { if (selectedItemName != null) {
selectItemByName(selectedItemName); selectItemByName(selectedItemName);
} }
@ -716,7 +716,7 @@ public class FilePanelTab extends JPanel {
updateStatus(); updateStatus();
// Oznámit změnu adresáře // Notify directory change
if (onDirectoryChanged != null) { if (onDirectoryChanged != null) {
onDirectoryChanged.run(); onDirectoryChanged.run();
} }
@ -1207,8 +1207,8 @@ public class FilePanelTab extends JPanel {
private void updateColumnRenderers() { private void updateColumnRenderers() {
int columnCount = tableModel.getColumnCount(); int columnCount = tableModel.getColumnCount();
if (columnCount == 0 || fileTable.getColumnModel().getColumnCount() != columnCount) { if (columnCount == 0 || fileTable.getColumnModel().getColumnCount() != columnCount) {
// Sloupce tabulky se možná ještě nepřevedly po změně struktury. // Table columns might not have been converted yet after structure change.
// Zkusíme to znovu asynchronně po krátkém odložení. // Try again asynchronously after a short delay.
SwingUtilities.invokeLater(() -> updateColumnRenderers()); SwingUtilities.invokeLater(() -> updateColumnRenderers());
return; return;
} }
@ -1296,7 +1296,7 @@ public class FilePanelTab extends JPanel {
} }
} }
// Zobrazit ikonu pro názvy souborů // Show icon for file names
Icon icon = item.getIcon(); Icon icon = item.getIcon();
if (item.getName().equals("..")) { if (item.getName().equals("..")) {
icon = new UpArrowIcon(getForeground()); icon = new UpArrowIcon(getForeground());
@ -1305,10 +1305,10 @@ public class FilePanelTab extends JPanel {
} }
if (viewMode == ViewMode.BRIEF) { if (viewMode == ViewMode.BRIEF) {
// V BRIEF módu jsou všechny sloupce názvy // In BRIEF mode all columns are names
setIcon(icon); setIcon(icon);
} else if (viewMode == ViewMode.FULL && column == 0) { } else if (viewMode == ViewMode.FULL && column == 0) {
// V FULL módu je ikona pouze v prvním sloupci (názvy) // In FULL mode the icon is only in the first column (names)
setIcon(icon); setIcon(icon);
} else { } else {
setIcon(null); setIcon(null);
@ -1340,9 +1340,9 @@ public class FilePanelTab extends JPanel {
return; return;
} }
// Pokud se ColumnModel ještě nepřizpůsobil nové struktuře (např. po fireTableStructureChanged()), // If ColumnModel has not yet adapted to the new structure (e.g. after fireTableStructureChanged()),
// počkej a zkus nastavit renderery později - jinak nové sloupce nedostanou renderer a ikony se // wait and try to set renderers later - otherwise new columns won't get a renderer and icons won't
// neprojeví v FULL módu. // show up in FULL mode.
if (fileTable.getColumnModel().getColumnCount() != columnCount) { if (fileTable.getColumnModel().getColumnCount() != columnCount) {
SwingUtilities.invokeLater(() -> updateColumnRenderers()); SwingUtilities.invokeLater(() -> updateColumnRenderers());
return; return;
@ -1423,7 +1423,7 @@ public class FilePanelTab extends JPanel {
} }
if (markedCount > 0) { if (markedCount > 0) {
statusLabel.setText(String.format(" Označeno: %d souborů, %d adresářů (%s)", statusLabel.setText(String.format(" Selected: %d files, %d directories (%s)",
fileCount, dirCount, formatSize(totalSize))); fileCount, dirCount, formatSize(totalSize)));
} else { } else {
int selectedRow = fileTable.getSelectedRow(); int selectedRow = fileTable.getSelectedRow();
@ -1448,10 +1448,10 @@ public class FilePanelTab extends JPanel {
item.getFormattedDate())); item.getFormattedDate()));
} }
} else { } else {
statusLabel.setText(String.format(" Položek: %d", tableModel.items.size())); statusLabel.setText(String.format(" Items: %d", tableModel.items.size()));
} }
} else { } else {
statusLabel.setText(String.format(" Položek: %d", tableModel.items.size())); statusLabel.setText(String.format(" Items: %d", tableModel.items.size()));
} }
} }
} }
@ -1794,7 +1794,7 @@ public class FilePanelTab extends JPanel {
} }
} }
// Gettery // Getters
public JTable getFileTable() { public JTable getFileTable() {
return fileTable; return fileTable;
} }
@ -1807,10 +1807,10 @@ public class FilePanelTab extends JPanel {
return viewMode; return viewMode;
} }
// FileTableModel - stejný jako v původním FilePanel // FileTableModel
private class FileTableModel extends AbstractTableModel { private class FileTableModel extends AbstractTableModel {
private List<FileItem> items = new ArrayList<>(); private List<FileItem> items = new ArrayList<>();
private String[] columnNames = {"Název", "Velikost", "Datum"}; private String[] columnNames = {"Name", "Size", "Date"};
public int briefColumns = 1; public int briefColumns = 1;
public int briefRowsPerColumn = 10; public int briefRowsPerColumn = 10;

View File

@ -20,7 +20,7 @@ public class MainWindow extends JFrame {
private FilePanel rightPanel; private FilePanel rightPanel;
private FilePanel activePanel; private FilePanel activePanel;
private JPanel buttonPanel; private JPanel buttonPanel;
private JTextField commandLine; private JComboBox<String> commandLine;
private AppConfig config; private AppConfig config;
public MainWindow() { public MainWindow() {
@ -75,7 +75,7 @@ public class MainWindow extends JFrame {
ViewMode leftViewMode = ViewMode.valueOf(config.getLeftPanelViewMode()); ViewMode leftViewMode = ViewMode.valueOf(config.getLeftPanelViewMode());
leftPanel.setViewMode(leftViewMode); leftPanel.setViewMode(leftViewMode);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// Výchozí hodnota FULL je již nastavena // Default value FULL is already set
} }
// Right panel - load path and ViewMode from configuration // Right panel - load path and ViewMode from configuration
@ -91,7 +91,7 @@ public class MainWindow extends JFrame {
ViewMode rightViewMode = ViewMode.valueOf(config.getRightPanelViewMode()); ViewMode rightViewMode = ViewMode.valueOf(config.getRightPanelViewMode());
rightPanel.setViewMode(rightViewMode); rightPanel.setViewMode(rightViewMode);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
// Výchozí hodnota FULL je již nastavena // Default value FULL is already set
} }
mainPanel.add(leftPanel); mainPanel.add(leftPanel);
@ -146,19 +146,19 @@ public class MainWindow extends JFrame {
public void focusGained(FocusEvent e) { public void focusGained(FocusEvent e) {
activePanel = leftPanel; activePanel = leftPanel;
updateActivePanelBorder(); updateActivePanelBorder();
// Zajistit, že je vybrán nějaký řádek // Ensure some row is selected
JTable leftTable = leftPanel.getFileTable(); JTable leftTable = leftPanel.getFileTable();
if (leftTable.getSelectedRow() == -1 && leftTable.getRowCount() > 0) { if (leftTable.getSelectedRow() == -1 && leftTable.getRowCount() > 0) {
leftTable.setRowSelectionInterval(0, 0); leftTable.setRowSelectionInterval(0, 0);
} }
// Překreslit oba panely // Repaint both panels
leftPanel.getFileTable().repaint(); leftPanel.getFileTable().repaint();
rightPanel.getFileTable().repaint(); rightPanel.getFileTable().repaint();
} }
@Override @Override
public void focusLost(FocusEvent e) { public void focusLost(FocusEvent e) {
// Překreslit při ztrátě focusu // Repaint on focus loss
leftPanel.getFileTable().repaint(); leftPanel.getFileTable().repaint();
} }
}); });
@ -168,19 +168,19 @@ public class MainWindow extends JFrame {
public void focusGained(FocusEvent e) { public void focusGained(FocusEvent e) {
activePanel = rightPanel; activePanel = rightPanel;
updateActivePanelBorder(); updateActivePanelBorder();
// Zajistit, že je vybrán nějaký řádek // Ensure some row is selected
JTable rightTable = rightPanel.getFileTable(); JTable rightTable = rightPanel.getFileTable();
if (rightTable.getSelectedRow() == -1 && rightTable.getRowCount() > 0) { if (rightTable.getSelectedRow() == -1 && rightTable.getRowCount() > 0) {
rightTable.setRowSelectionInterval(0, 0); rightTable.setRowSelectionInterval(0, 0);
} }
// Překreslit oba panely // Repaint both panels
leftPanel.getFileTable().repaint(); leftPanel.getFileTable().repaint();
rightPanel.getFileTable().repaint(); rightPanel.getFileTable().repaint();
} }
@Override @Override
public void focusLost(FocusEvent e) { public void focusLost(FocusEvent e) {
// Překreslit při ztrátě focusu // Repaint on focus loss
rightPanel.getFileTable().repaint(); rightPanel.getFileTable().repaint();
} }
}); });
@ -218,28 +218,42 @@ public class MainWindow extends JFrame {
cmdLabel.setFont(new Font("Monospaced", Font.BOLD, 12)); cmdLabel.setFont(new Font("Monospaced", Font.BOLD, 12));
cmdPanel.add(cmdLabel, BorderLayout.WEST); cmdPanel.add(cmdLabel, BorderLayout.WEST);
commandLine = new JTextField(); commandLine = new JComboBox<>();
commandLine.setEditable(true);
commandLine.setFont(new Font("Monospaced", Font.PLAIN, 12)); commandLine.setFont(new Font("Monospaced", Font.PLAIN, 12));
commandLine.setFocusTraversalKeysEnabled(false);
commandLine.addActionListener(e -> executeCommand(commandLine.getText()));
// Let the panels catch focus back if user presses ESC or TAB in command line // Handle the editor component (usually a JTextField)
commandLine.addKeyListener(new KeyAdapter() { Component editorComp = commandLine.getEditor().getEditorComponent();
@Override if (editorComp instanceof JTextField) {
public void keyPressed(KeyEvent e) { JTextField tf = (JTextField) editorComp;
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { tf.setFocusTraversalKeysEnabled(false);
commandLine.setText(""); tf.addActionListener(e -> executeCommand(tf.getText()));
activePanel.getFileTable().requestFocusInWindow();
e.consume(); // Let the panels catch focus back if user presses ESC or TAB in command line
} else if (e.getKeyCode() == KeyEvent.VK_TAB) { tf.addKeyListener(new KeyAdapter() {
activePanel.getFileTable().requestFocusInWindow(); @Override
e.consume(); public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
tf.setText("");
activePanel.getFileTable().requestFocusInWindow();
e.consume();
} else if (e.getKeyCode() == KeyEvent.VK_TAB) {
activePanel.getFileTable().requestFocusInWindow();
e.consume();
}
} }
} });
}); }
cmdPanel.add(commandLine, BorderLayout.CENTER); cmdPanel.add(commandLine, BorderLayout.CENTER);
// Load history from config
java.util.List<String> history = config.getCommandLineHistory();
for (int i = 0; i < history.size(); i++) {
commandLine.addItem(history.get(i));
}
commandLine.getEditor().setItem(""); // Ensure it starts empty
bottomContainer.add(cmdPanel, BorderLayout.NORTH); bottomContainer.add(cmdPanel, BorderLayout.NORTH);
// Bottom panel with buttons // Bottom panel with buttons
@ -285,7 +299,7 @@ public class MainWindow extends JFrame {
toolBar.addSeparator(); toolBar.addSeparator();
// Informační label // Info label
JLabel infoLabel = new JLabel(" View Mode "); JLabel infoLabel = new JLabel(" View Mode ");
infoLabel.setFont(infoLabel.getFont().deriveFont(Font.PLAIN, 10f)); infoLabel.setFont(infoLabel.getFont().deriveFont(Font.PLAIN, 10f));
toolBar.add(infoLabel); toolBar.add(infoLabel);
@ -493,7 +507,7 @@ public class MainWindow extends JFrame {
private void setupKeyBindings() { private void setupKeyBindings() {
JRootPane rootPane = getRootPane(); JRootPane rootPane = getRootPane();
// F3 - Prohlížeč // F3 - Viewer
rootPane.registerKeyboardAction(e -> viewFile(), rootPane.registerKeyboardAction(e -> viewFile(),
KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0), KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0),
JComponent.WHEN_IN_FOCUSED_WINDOW); JComponent.WHEN_IN_FOCUSED_WINDOW);
@ -560,7 +574,7 @@ public class MainWindow extends JFrame {
rootPane.registerKeyboardAction(e -> { rootPane.registerKeyboardAction(e -> {
if (activePanel != null) { if (activePanel != null) {
// Always clear command line and return focus to panels // Always clear command line and return focus to panels
commandLine.setText(""); commandLine.getEditor().setItem("");
activePanel.getFileTable().requestFocusInWindow(); activePanel.getFileTable().requestFocusInWindow();
} }
}, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), }, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
@ -616,6 +630,11 @@ public class MainWindow extends JFrame {
rootPane.registerKeyboardAction(e -> closeCurrentTabInActivePanel(), rootPane.registerKeyboardAction(e -> closeCurrentTabInActivePanel(),
KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.CTRL_DOWN_MASK), KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.CTRL_DOWN_MASK),
JComponent.WHEN_IN_FOCUSED_WINDOW); JComponent.WHEN_IN_FOCUSED_WINDOW);
// Ctrl+E - Command line history
rootPane.registerKeyboardAction(e -> showCommandLineHistory(),
KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK),
JComponent.WHEN_IN_FOCUSED_WINDOW);
} }
/** /**
@ -687,7 +706,7 @@ public class MainWindow extends JFrame {
* Attach TAB handling to switch panels * Attach TAB handling to switch panels
*/ */
private void addTabKeyHandler(JTable table) { private void addTabKeyHandler(JTable table) {
// Odstraníme standardní chování TAB ve Swing // Remove standard Swing TAB behavior
table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "switchPanel"); .put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), "switchPanel");
table.getActionMap().put("switchPanel", new AbstractAction() { table.getActionMap().put("switchPanel", new AbstractAction() {
@ -709,7 +728,7 @@ public class MainWindow extends JFrame {
} }
}); });
// Přidáme F8 pro mazání s vyšší prioritou než defaultní Swing akce // Add F8 for deleting with higher priority than default Swing actions
table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0), "deleteFiles"); .put(KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0), "deleteFiles");
table.getActionMap().put("deleteFiles", new AbstractAction() { table.getActionMap().put("deleteFiles", new AbstractAction() {
@ -757,7 +776,8 @@ public class MainWindow extends JFrame {
// Printable characters only (exclude control keys like Enter, Backspace, Esc, Tab) // Printable characters only (exclude control keys like Enter, Backspace, Esc, Tab)
if (c != KeyEvent.CHAR_UNDEFINED && c != '\b' && c != '\n' && c != '\t' && c != 27) { if (c != KeyEvent.CHAR_UNDEFINED && c != '\b' && c != '\n' && c != '\t' && c != 27) {
commandLine.requestFocusInWindow(); commandLine.requestFocusInWindow();
commandLine.setText(commandLine.getText() + c); String current = commandLine.getEditor().getItem().toString();
commandLine.getEditor().setItem(current + c);
e.consume(); e.consume();
} }
} }
@ -767,7 +787,7 @@ public class MainWindow extends JFrame {
private void copyFocusedToCommandLine(boolean fullPath) { private void copyFocusedToCommandLine(boolean fullPath) {
FileItem focused = activePanel.getFocusedItem(); FileItem focused = activePanel.getFocusedItem();
if (focused != null && !focused.getName().equals("..")) { if (focused != null && !focused.getName().equals("..")) {
String current = commandLine.getText(); String current = commandLine.getEditor().getItem().toString();
String toAdd = fullPath ? focused.getFile().getAbsolutePath() : focused.getName(); String toAdd = fullPath ? focused.getFile().getAbsolutePath() : focused.getName();
// If it contains spaces, wrap in quotes // If it contains spaces, wrap in quotes
@ -776,9 +796,9 @@ public class MainWindow extends JFrame {
} }
if (!current.isEmpty() && !current.endsWith(" ")) { if (!current.isEmpty() && !current.endsWith(" ")) {
commandLine.setText(current + " " + toAdd); commandLine.getEditor().setItem(current + " " + toAdd);
} else { } else {
commandLine.setText(current + toAdd); commandLine.getEditor().setItem(current + toAdd);
} }
commandLine.requestFocusInWindow(); commandLine.requestFocusInWindow();
} }
@ -808,7 +828,7 @@ public class MainWindow extends JFrame {
if (result == JOptionPane.OK_OPTION) { if (result == JOptionPane.OK_OPTION) {
performFileOperation((callback) -> { performFileOperation((callback) -> {
FileOperations.copy(selectedItems, targetDir, callback); FileOperations.copy(selectedItems, targetDir, callback);
}, "Kopírování dokončeno", true, targetPanel); }, "Copy completed", true, targetPanel);
} }
} }
@ -836,7 +856,7 @@ public class MainWindow extends JFrame {
if (result == JOptionPane.OK_OPTION) { if (result == JOptionPane.OK_OPTION) {
performFileOperation((callback) -> { performFileOperation((callback) -> {
FileOperations.move(selectedItems, targetDir, callback); FileOperations.move(selectedItems, targetDir, callback);
}, "Přesouvání dokončeno", false, activePanel, targetPanel); }, "Move completed", false, activePanel, targetPanel);
} }
} }
@ -867,14 +887,14 @@ public class MainWindow extends JFrame {
int result = JOptionPane.showConfirmDialog(this, int result = JOptionPane.showConfirmDialog(this,
message.toString(), message.toString(),
"Mazání", "Delete",
JOptionPane.YES_NO_OPTION, JOptionPane.YES_NO_OPTION,
JOptionPane.WARNING_MESSAGE); JOptionPane.WARNING_MESSAGE);
if (result == JOptionPane.YES_OPTION) { if (result == JOptionPane.YES_OPTION) {
performFileOperation((callback) -> { performFileOperation((callback) -> {
FileOperations.delete(selectedItems, callback); FileOperations.delete(selectedItems, callback);
}, "Mazání dokončeno", false, activePanel); }, "Delete completed", false, activePanel);
// After deletion and refresh, restore selection: stay on same row if possible, // After deletion and refresh, restore selection: stay on same row if possible,
// otherwise move selection one row up. // otherwise move selection one row up.
@ -949,7 +969,7 @@ public class MainWindow extends JFrame {
final File finalTargetZip = targetZip; final File finalTargetZip = targetZip;
performFileOperation((callback) -> { performFileOperation((callback) -> {
FileOperations.zip(selectedItems, finalTargetZip, callback); FileOperations.zip(selectedItems, finalTargetZip, callback);
}, "Zabaleno do " + zipName, false, targetPanel); }, "Zipped into " + zipName, false, targetPanel);
} }
/** /**
@ -985,7 +1005,7 @@ public class MainWindow extends JFrame {
if (result == JOptionPane.OK_OPTION) { if (result == JOptionPane.OK_OPTION) {
performFileOperation((callback) -> { performFileOperation((callback) -> {
FileOperations.unzip(zipFile, targetDir, callback); FileOperations.unzip(zipFile, targetDir, callback);
}, "Rozbaleno do " + targetDir.getName(), false, targetPanel); }, "Unzipped into " + targetDir.getName(), false, targetPanel);
} }
} }
@ -1014,7 +1034,7 @@ public class MainWindow extends JFrame {
if (newName != null && !newName.trim().isEmpty() && !newName.equals(item.getName())) { if (newName != null && !newName.trim().isEmpty() && !newName.equals(item.getName())) {
performFileOperation((callback) -> { performFileOperation((callback) -> {
FileOperations.rename(item.getFile(), newName.trim()); FileOperations.rename(item.getFile(), newName.trim());
}, "Přejmenování dokončeno", false, activePanel); }, "Rename completed", false, activePanel);
} }
} }
} }
@ -1139,8 +1159,8 @@ public class MainWindow extends JFrame {
} catch (Exception ex) { } catch (Exception ex) {
// Fall back to internal editor if external fails // Fall back to internal editor if external fails
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
"Nelze spustit externí editor: " + ex.getMessage() + "\nPoužije se interní editor.", "Could not start external editor: " + ex.getMessage() + "\nUsing internal editor.",
"Chyba", JOptionPane.ERROR_MESSAGE); "Error", JOptionPane.ERROR_MESSAGE);
} }
} }
@ -1153,8 +1173,8 @@ public class MainWindow extends JFrame {
* Refresh both panels * Refresh both panels
*/ */
private void refreshPanels() { private void refreshPanels() {
// Refresh je nyní automatický při změnách // Refresh is now automatic upon changes
// Pokud je potřeba manuální refresh, můžeme zavolat loadDirectory // If manual refresh is needed, we can call loadDirectory
if (leftPanel.getCurrentDirectory() != null) { if (leftPanel.getCurrentDirectory() != null) {
leftPanel.loadDirectory(leftPanel.getCurrentDirectory()); leftPanel.loadDirectory(leftPanel.getCurrentDirectory());
} }
@ -1172,6 +1192,16 @@ public class MainWindow extends JFrame {
} }
} }
/**
* Show history of command line
*/
private void showCommandLineHistory() {
if (commandLine != null) {
commandLine.requestFocusInWindow();
commandLine.showPopup();
}
}
/** /**
* Add a new tab to the active panel * Add a new tab to the active panel
*/ */
@ -1247,8 +1277,8 @@ public class MainWindow extends JFrame {
} catch (Exception e) { } catch (Exception e) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
"Chyba při otevírání terminálu: " + e.getMessage(), "Error opening terminal: " + e.getMessage(),
"Chyba", "Error",
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
} }
} }
@ -1259,6 +1289,9 @@ public class MainWindow extends JFrame {
return; return;
} }
// Add to history
addCommandToHistory(command.trim());
File currentDir = activePanel.getCurrentDirectory(); File currentDir = activePanel.getCurrentDirectory();
if (currentDir == null) { if (currentDir == null) {
currentDir = new File(System.getProperty("user.home")); currentDir = new File(System.getProperty("user.home"));
@ -1309,18 +1342,39 @@ public class MainWindow extends JFrame {
if (pb != null) { if (pb != null) {
pb.directory(currentDir); pb.directory(currentDir);
pb.start(); pb.start();
commandLine.setText(""); // Clear after execution
// Clear after execution
Component editorComp = commandLine.getEditor().getEditorComponent();
if (editorComp instanceof JTextField) {
((JTextField) editorComp).setText("");
} else {
commandLine.setSelectedItem("");
}
activePanel.getFileTable().requestFocusInWindow(); activePanel.getFileTable().requestFocusInWindow();
} }
} catch (Exception e) { } catch (Exception e) {
JOptionPane.showMessageDialog(this, JOptionPane.showMessageDialog(this,
"Chyba při spouštění příkazu: " + e.getMessage(), "Error executing command: " + e.getMessage(),
"Chyba", "Error",
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
} }
} }
private void addCommandToHistory(String command) {
if (command == null || command.isEmpty()) return;
// Remove if already exists to move it to the top
for (int i = 0; i < commandLine.getItemCount(); i++) {
if (command.equals(commandLine.getItemAt(i))) {
commandLine.removeItemAt(i);
break;
}
}
commandLine.insertItemAt(command, 0);
// We don't necessarily want to select it here as it might interfere with the editor state
}
/** /**
* Show About dialog * Show About dialog
*/ */
@ -1351,7 +1405,7 @@ public class MainWindow extends JFrame {
* Execute file operation with error handling * Execute file operation with error handling
*/ */
private void performFileOperation(FileOperation operation, String successMessage, boolean showBytes, FilePanel... panelsToRefresh) { private void performFileOperation(FileOperation operation, String successMessage, boolean showBytes, FilePanel... panelsToRefresh) {
ProgressDialog progressDialog = new ProgressDialog(this, "Operační systém"); ProgressDialog progressDialog = new ProgressDialog(this, "File Operation");
progressDialog.setDisplayAsBytes(showBytes); progressDialog.setDisplayAsBytes(showBytes);
FileOperations.ProgressCallback callback = new FileOperations.ProgressCallback() { FileOperations.ProgressCallback callback = new FileOperations.ProgressCallback() {
@ -1379,15 +1433,15 @@ public class MainWindow extends JFrame {
} }
} }
if (callback.isCancelled()) { if (callback.isCancelled()) {
JOptionPane.showMessageDialog(MainWindow.this, "Operace byla přerušena uživatelem."); JOptionPane.showMessageDialog(MainWindow.this, "Operation was cancelled by user.");
} }
}); });
} catch (Exception e) { } catch (Exception e) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
progressDialog.dispose(); progressDialog.dispose();
JOptionPane.showMessageDialog(MainWindow.this, JOptionPane.showMessageDialog(MainWindow.this,
"Chyba: " + e.getMessage(), "Error: " + e.getMessage(),
"Chyba", "Error",
JOptionPane.ERROR_MESSAGE); JOptionPane.ERROR_MESSAGE);
}); });
} }
@ -1446,7 +1500,14 @@ public class MainWindow extends JFrame {
// ignore // ignore
} }
// Uložit konfiguraci do souboru // Save command history
java.util.List<String> cmdHistory = new java.util.ArrayList<>();
for (int i = 0; i < commandLine.getItemCount(); i++) {
cmdHistory.add(commandLine.getItemAt(i));
}
config.saveCommandLineHistory(cmdHistory);
// Save configuration to file
config.saveConfig(); config.saveConfig();
// Exit application // Exit application