diff --git a/src/main/java/cz/kamma/kfmanager/MainApp.java b/src/main/java/cz/kamma/kfmanager/MainApp.java index 901776f..c71c134 100644 --- a/src/main/java/cz/kamma/kfmanager/MainApp.java +++ b/src/main/java/cz/kamma/kfmanager/MainApp.java @@ -15,7 +15,7 @@ import java.io.InputStreamReader; */ public class MainApp { - public static final String APP_VERSION = "1.1.4"; + public static final String APP_VERSION = "1.1.5"; public enum OS { WINDOWS, LINUX, MACOS, UNKNOWN diff --git a/src/main/java/cz/kamma/kfmanager/service/FileOperations.java b/src/main/java/cz/kamma/kfmanager/service/FileOperations.java index 5f97c84..19653b6 100644 --- a/src/main/java/cz/kamma/kfmanager/service/FileOperations.java +++ b/src/main/java/cz/kamma/kfmanager/service/FileOperations.java @@ -1,9 +1,21 @@ package cz.kamma.kfmanager.service; -import cz.kamma.kfmanager.model.FileItem; - -import java.io.*; -import java.nio.file.*; +import java.io.BufferedReader; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.nio.file.AccessDeniedException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.LinkOption; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardCopyOption; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; @@ -11,9 +23,10 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; -import java.util.regex.Matcher; -import java.util.zip.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import cz.kamma.kfmanager.model.FileItem; import net.lingala.zip4j.ZipFile; import net.lingala.zip4j.model.FileHeader; diff --git a/src/main/java/cz/kamma/kfmanager/ui/FilePanel.java b/src/main/java/cz/kamma/kfmanager/ui/FilePanel.java index 751f152..7a84f2d 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/FilePanel.java +++ b/src/main/java/cz/kamma/kfmanager/ui/FilePanel.java @@ -200,6 +200,11 @@ public class FilePanel extends JPanel { tabbedPane.addChangeListener(e -> { updatePathField(); updateTabStyles(); + // Automatically focus the table in the newly selected tab + FilePanelTab tab = getCurrentTab(); + if (tab != null) { + tab.getFileTable().requestFocusInWindow(); + } }); add(tabbedPane, BorderLayout.CENTER); @@ -219,7 +224,9 @@ public class FilePanel extends JPanel { boolean isInteractive = comp instanceof JButton || comp instanceof JComboBox || comp instanceof JTextField || - comp instanceof JTable; + comp instanceof JTable || + comp instanceof JScrollBar || + comp instanceof javax.swing.table.JTableHeader; if (!isInteractive) { comp.addMouseListener(new java.awt.event.MouseAdapter() { diff --git a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java index 35c61fd..2578d31 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java +++ b/src/main/java/cz/kamma/kfmanager/ui/FilePanelTab.java @@ -292,6 +292,26 @@ public class FilePanelTab extends JPanel { // Also override mouse-motion processing to suppress drag events and // prevent any drag-and-drop transfer handling. fileTable = new JTable(tableModel) { + @Override + public int rowAtPoint(Point p) { + int r = super.rowAtPoint(p); + int c = super.columnAtPoint(p); + if (viewMode == ViewMode.BRIEF && r >= 0 && c >= 0) { + if (tableModel.getItemFromBriefLayout(r, c) == null) return -1; + } + return r; + } + + @Override + public int columnAtPoint(Point p) { + int c = super.columnAtPoint(p); + int r = super.rowAtPoint(p); + if (viewMode == ViewMode.BRIEF && r >= 0 && c >= 0) { + if (tableModel.getItemFromBriefLayout(r, c) == null) return -1; + } + return c; + } + @Override public void editingStopped(javax.swing.event.ChangeEvent e) { super.editingStopped(e); @@ -657,6 +677,30 @@ public class FilePanelTab extends JPanel { fileTable.setOpaque(true); fileTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + // Ensure selection when table gains focus + fileTable.addFocusListener(new java.awt.event.FocusAdapter() { + @Override + public void focusGained(java.awt.event.FocusEvent e) { + if (fileTable.getSelectionModel().isSelectionEmpty() && fileTable.getRowCount() > 0) { + int targetRow = Math.min(lastValidRow, fileTable.getRowCount() - 1); + if (targetRow < 0) targetRow = 0; + final int finalRow = targetRow; + final int finalCol = lastValidBriefColumn; + SwingUtilities.invokeLater(() -> { + if (fileTable != null && fileTable.getSelectionModel().isSelectionEmpty() && fileTable.getRowCount() > 0) { + briefCurrentColumn = finalCol; + fileTable.setRowSelectionInterval(finalRow, finalRow); + try { + fileTable.scrollRectToVisible(fileTable.getCellRect(finalRow, finalCol, true)); + } catch (Exception ignore) {} + updateStatus(); + } + }); + } + } + }); + // Enforce that at least one item is always active (selected) if the table is not empty. fileTable.getSelectionModel().addListSelectionListener(e -> { int row = fileTable.getSelectedRow(); @@ -3265,8 +3309,16 @@ public class FilePanelTab extends JPanel { return; } - briefRowsPerColumn = Math.max(1, availableHeight / rowHeight); - briefColumns = (int) Math.ceil((double) items.size() / briefRowsPerColumn); + int viewportRows = Math.max(1, availableHeight / rowHeight); + if (items.size() <= viewportRows) { + // If everything fits in one column, use only necessary rows + briefRowsPerColumn = Math.max(1, items.size()); + briefColumns = 1; + } else { + // Multi-column mode: use full height available + briefRowsPerColumn = viewportRows; + briefColumns = (int) Math.ceil((double) items.size() / briefRowsPerColumn); + } // Calculate extra columns to allow snapping correctly at the end. // We want the scrolling to stop precisely when the last column is fully visible diff --git a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java index 2dfaea6..da6abb2 100644 --- a/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java +++ b/src/main/java/cz/kamma/kfmanager/ui/MainWindow.java @@ -121,11 +121,9 @@ public class MainWindow extends JFrame { leftPanel.setSwitchPanelCallback(() -> switchPanelsFromChild()); leftPanel.setOnDirectoryChangedAll(() -> updateCommandLinePrompt()); leftPanel.setOnTableCreated(table -> { - addTabKeyHandler(table); - addCommandLineRedirect(table); + setupFileTable(table, leftPanel); }); - addCommandLineRedirect(leftPanel.getFileTable()); - addTabKeyHandler(leftPanel.getFileTable()); + setupFileTable(leftPanel.getFileTable(), leftPanel); // Load and set ViewMode for left panel try { @@ -143,11 +141,9 @@ public class MainWindow extends JFrame { rightPanel.setSwitchPanelCallback(() -> switchPanelsFromChild()); rightPanel.setOnDirectoryChangedAll(() -> updateCommandLinePrompt()); rightPanel.setOnTableCreated(table -> { - addTabKeyHandler(table); - addCommandLineRedirect(table); + setupFileTable(table, rightPanel); }); - addCommandLineRedirect(rightPanel.getFileTable()); - addTabKeyHandler(rightPanel.getFileTable()); + setupFileTable(rightPanel.getFileTable(), rightPanel); // Load and set ViewMode for right panel try { @@ -275,61 +271,6 @@ public class MainWindow extends JFrame { } }); - // Focus listeners to track active panel and ensure selection - leftPanel.getFileTable().addFocusListener(new FocusAdapter() { - @Override - public void focusGained(FocusEvent e) { - activePanel = leftPanel; - updateActivePanelBorder(); - updateCommandLinePrompt(); - // Ensure some row is selected - JTable leftTable = leftPanel.getFileTable(); - if (leftTable.getSelectedRow() == -1 && leftTable.getRowCount() > 0) { - leftTable.setRowSelectionInterval(0, 0); - } - } - - @Override - public void focusLost(FocusEvent e) { - // Repaint on focus loss - leftPanel.getFileTable().repaint(); - } - }); - - // Add selection listeners for Quick View updates - leftPanel.getFileTable().getSelectionModel().addListSelectionListener(e -> { - if (!e.getValueIsAdjusting() && activePanel == leftPanel) { - updateQuickViewInfo(); - } - }); - - rightPanel.getFileTable().addFocusListener(new FocusAdapter() { - @Override - public void focusGained(FocusEvent e) { - activePanel = rightPanel; - updateActivePanelBorder(); - updateCommandLinePrompt(); - // Ensure some row is selected - JTable rightTable = rightPanel.getFileTable(); - if (rightTable.getSelectedRow() == -1 && rightTable.getRowCount() > 0) { - rightTable.setRowSelectionInterval(0, 0); - } - } - - @Override - public void focusLost(FocusEvent e) { - // Repaint on focus loss - rightPanel.getFileTable().repaint(); - } - }); - - // Add selection listeners for Quick View updates - rightPanel.getFileTable().getSelectionModel().addListSelectionListener(e -> { - if (!e.getValueIsAdjusting() && activePanel == rightPanel) { - updateQuickViewInfo(); - } - }); - // Click on panel anywhere should request focus to its table leftPanel.addMouseListener(new MouseAdapter() { @Override @@ -1456,6 +1397,42 @@ public class MainWindow extends JFrame { cmdLabel.setText(path + ">"); } + /** + * Attach all necessary listeners and handlers to a file table + */ + private void setupFileTable(JTable table, FilePanel panel) { + if (table == null) return; + + addTabKeyHandler(table); + addCommandLineRedirect(table); + + // Focus listener to track active panel and ensure selection + table.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + activePanel = panel; + updateActivePanelBorder(); + updateCommandLinePrompt(); + // Ensure some row is selected + if (table.getSelectedRow() == -1 && table.getRowCount() > 0) { + table.setRowSelectionInterval(0, 0); + } + } + + @Override + public void focusLost(FocusEvent e) { + table.repaint(); + } + }); + + // Selection listener for Quick View updates + table.getSelectionModel().addListSelectionListener(e -> { + if (!e.getValueIsAdjusting() && activePanel == panel) { + updateQuickViewInfo(); + } + }); + } + /** * Attach TAB handling to switch panels */