added item info ctrl+q
This commit is contained in:
parent
7962d8defc
commit
609e96145d
@ -745,6 +745,18 @@ public class FilePanel extends JPanel {
|
|||||||
FilePanelTab tab = getCurrentTab();
|
FilePanelTab tab = getCurrentTab();
|
||||||
return tab != null ? tab.getViewMode() : ViewMode.FULL;
|
return tab != null ? tab.getViewMode() : ViewMode.FULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ViewMode getPreviousViewMode() {
|
||||||
|
FilePanelTab tab = getCurrentTab();
|
||||||
|
return tab != null ? tab.getPreviousViewMode() : ViewMode.FULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInfoItem(FileItem item) {
|
||||||
|
FilePanelTab tab = getCurrentTab();
|
||||||
|
if (tab != null) {
|
||||||
|
tab.setInfoItem(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void loadDirectory(File directory) {
|
public void loadDirectory(File directory) {
|
||||||
loadDirectory(directory, true);
|
loadDirectory(directory, true);
|
||||||
|
|||||||
@ -39,6 +39,10 @@ import com.github.junrar.rarfile.FileHeader;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
|
import java.nio.file.attribute.BasicFileAttributes;
|
||||||
|
import java.nio.file.attribute.PosixFileAttributes;
|
||||||
|
import java.nio.file.attribute.PosixFilePermissions;
|
||||||
|
import java.nio.file.attribute.DosFileAttributes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Single tab in a panel - displays the contents of one directory
|
* Single tab in a panel - displays the contents of one directory
|
||||||
@ -50,6 +54,11 @@ public class FilePanelTab extends JPanel {
|
|||||||
private FileTableModel tableModel;
|
private FileTableModel tableModel;
|
||||||
private JLabel statusLabel;
|
private JLabel statusLabel;
|
||||||
private ViewMode viewMode = ViewMode.FULL;
|
private ViewMode viewMode = ViewMode.FULL;
|
||||||
|
private ViewMode previousViewMode = ViewMode.FULL; // Store mode before switching to INFO
|
||||||
|
private JPanel cardPanel;
|
||||||
|
private CardLayout cardLayout;
|
||||||
|
private JTextArea infoTextArea;
|
||||||
|
private JScrollPane infoScrollPane;
|
||||||
private int briefCurrentColumn = 0;
|
private int briefCurrentColumn = 0;
|
||||||
private Runnable onDirectoryChanged;
|
private Runnable onDirectoryChanged;
|
||||||
private Runnable onSwitchPanelRequested;
|
private Runnable onSwitchPanelRequested;
|
||||||
@ -170,6 +179,7 @@ public class FilePanelTab extends JPanel {
|
|||||||
public void applyGlobalFont(Font font) {
|
public void applyGlobalFont(Font font) {
|
||||||
if (font == null) return;
|
if (font == null) return;
|
||||||
fileTable.setFont(font);
|
fileTable.setFont(font);
|
||||||
|
infoTextArea.setFont(font);
|
||||||
statusLabel.setFont(font);
|
statusLabel.setFont(font);
|
||||||
// Update row height based on font metrics
|
// Update row height based on font metrics
|
||||||
FontMetrics fm = fileTable.getFontMetrics(font);
|
FontMetrics fm = fileTable.getFontMetrics(font);
|
||||||
@ -587,6 +597,18 @@ public class FilePanelTab extends JPanel {
|
|||||||
// Enable horizontal scrollbar when needed so BRIEF mode can scroll left-right
|
// Enable horizontal scrollbar when needed so BRIEF mode can scroll left-right
|
||||||
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
||||||
|
|
||||||
|
// Info panel for Quick View (Ctrl+Q)
|
||||||
|
infoTextArea = new JTextArea();
|
||||||
|
infoTextArea.setEditable(false);
|
||||||
|
infoTextArea.setFont(new Font("Monospaced", Font.PLAIN, 12));
|
||||||
|
infoTextArea.setMargin(new Insets(10, 10, 10, 10));
|
||||||
|
infoScrollPane = new JScrollPane(infoTextArea);
|
||||||
|
|
||||||
|
cardLayout = new CardLayout();
|
||||||
|
cardPanel = new JPanel(cardLayout);
|
||||||
|
cardPanel.add(scrollPane, "TABLE");
|
||||||
|
cardPanel.add(infoScrollPane, "INFO");
|
||||||
|
|
||||||
// Implement mouse wheel navigation in BRIEF and FULL mode to match arrow key behavior
|
// Implement mouse wheel navigation in BRIEF and FULL mode to match arrow key behavior
|
||||||
fileTable.addMouseWheelListener(new java.awt.event.MouseWheelListener() {
|
fileTable.addMouseWheelListener(new java.awt.event.MouseWheelListener() {
|
||||||
@Override
|
@Override
|
||||||
@ -613,7 +635,7 @@ public class FilePanelTab extends JPanel {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
add(scrollPane, BorderLayout.CENTER);
|
add(cardPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
// Status bar
|
// Status bar
|
||||||
statusLabel = new JLabel(" ");
|
statusLabel = new JLabel(" ");
|
||||||
@ -2311,13 +2333,16 @@ public class FilePanelTab extends JPanel {
|
|||||||
|
|
||||||
public void setViewMode(ViewMode mode, boolean requestFocus) {
|
public void setViewMode(ViewMode mode, boolean requestFocus) {
|
||||||
if (this.viewMode != mode) {
|
if (this.viewMode != mode) {
|
||||||
|
if (this.viewMode != ViewMode.INFO) {
|
||||||
|
this.previousViewMode = this.viewMode;
|
||||||
|
}
|
||||||
String selectedItemName = null;
|
String selectedItemName = null;
|
||||||
int selectedRow = fileTable.getSelectedRow();
|
int selectedRow = fileTable.getSelectedRow();
|
||||||
if (selectedRow >= 0) {
|
if (selectedRow >= 0) {
|
||||||
FileItem item = null;
|
FileItem item = null;
|
||||||
if (this.viewMode == ViewMode.BRIEF) {
|
if (this.viewMode == ViewMode.BRIEF) {
|
||||||
item = tableModel.getItemFromBriefLayout(selectedRow, briefCurrentColumn);
|
item = tableModel.getItemFromBriefLayout(selectedRow, briefCurrentColumn);
|
||||||
} else {
|
} else if (this.viewMode == ViewMode.FULL) {
|
||||||
item = tableModel.getItem(selectedRow);
|
item = tableModel.getItem(selectedRow);
|
||||||
}
|
}
|
||||||
if (item != null) {
|
if (item != null) {
|
||||||
@ -2329,35 +2354,142 @@ public class FilePanelTab extends JPanel {
|
|||||||
final String itemToSelect = selectedItemName;
|
final String itemToSelect = selectedItemName;
|
||||||
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
tableModel.updateViewMode(mode);
|
if (mode == ViewMode.INFO) {
|
||||||
// Switch auto-resize behavior depending on mode so BRIEF can scroll horizontally
|
cardLayout.show(cardPanel, "INFO");
|
||||||
if (mode == ViewMode.BRIEF) {
|
|
||||||
fileTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
|
||||||
} else {
|
} else {
|
||||||
fileTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
|
cardLayout.show(cardPanel, "TABLE");
|
||||||
}
|
tableModel.updateViewMode(mode);
|
||||||
// Hide table header in BRIEF mode to save vertical space and match requirements
|
// Switch auto-resize behavior depending on mode so BRIEF can scroll horizontally
|
||||||
if (fileTable.getTableHeader() != null) {
|
if (mode == ViewMode.BRIEF) {
|
||||||
fileTable.getTableHeader().setVisible(mode != ViewMode.BRIEF);
|
fileTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
||||||
}
|
} else {
|
||||||
briefCurrentColumn = 0;
|
fileTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
|
||||||
updateColumnRenderers();
|
}
|
||||||
updateColumnWidths();
|
// Hide table header in BRIEF mode to save vertical space and match requirements
|
||||||
fileTable.revalidate();
|
if (fileTable.getTableHeader() != null) {
|
||||||
fileTable.repaint();
|
fileTable.getTableHeader().setVisible(mode != ViewMode.BRIEF);
|
||||||
|
}
|
||||||
if (itemToSelect != null) {
|
briefCurrentColumn = 0;
|
||||||
selectItemByName(itemToSelect, requestFocus);
|
updateColumnRenderers();
|
||||||
} else if (fileTable.getRowCount() > 0) {
|
updateColumnWidths();
|
||||||
fileTable.setRowSelectionInterval(0, 0);
|
fileTable.revalidate();
|
||||||
|
fileTable.repaint();
|
||||||
|
|
||||||
|
if (itemToSelect != null) {
|
||||||
|
selectItemByName(itemToSelect, requestFocus);
|
||||||
|
} else if (fileTable.getRowCount() > 0) {
|
||||||
|
fileTable.setRowSelectionInterval(0, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (requestFocus) {
|
if (requestFocus) {
|
||||||
fileTable.requestFocusInWindow();
|
if (mode == ViewMode.INFO) {
|
||||||
|
infoTextArea.requestFocusInWindow();
|
||||||
|
} else {
|
||||||
|
fileTable.requestFocusInWindow();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ViewMode getViewMode() {
|
||||||
|
return viewMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ViewMode getPreviousViewMode() {
|
||||||
|
return previousViewMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setInfoItem(FileItem item) {
|
||||||
|
if (item == null || item.getName().equals("..")) {
|
||||||
|
infoTextArea.setText("No item selected.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File file = item.getFile();
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Name: ").append(file.getName()).append("\n");
|
||||||
|
sb.append("Path: ").append(file.getAbsolutePath()).append("\n\n");
|
||||||
|
sb.append("Modified: ").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(file.lastModified()))).append("\n");
|
||||||
|
|
||||||
|
if (!file.isDirectory()) {
|
||||||
|
sb.append("Type: File\n");
|
||||||
|
long size = file.length();
|
||||||
|
sb.append("Size: ").append(FileItem.formatSize(size)).append(" (").append(size).append(" bytes)\n");
|
||||||
|
addAttributes(sb, file);
|
||||||
|
infoTextArea.setText(sb.toString());
|
||||||
|
infoTextArea.setCaretPosition(0);
|
||||||
|
} else {
|
||||||
|
sb.append("Type: Directory\n");
|
||||||
|
infoTextArea.setText(sb.toString());
|
||||||
|
|
||||||
|
// Calculate size and count contents in background to avoid freezing UI
|
||||||
|
new SwingWorker<long[], Void>() {
|
||||||
|
@Override
|
||||||
|
protected long[] doInBackground() {
|
||||||
|
long[] results = new long[3]; // [size, filesCount, dirsCount]
|
||||||
|
calculateDirStats(file, results);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void done() {
|
||||||
|
try {
|
||||||
|
long[] res = get();
|
||||||
|
sb.append("Total Size: ").append(FileItem.formatSize(res[0])).append(" (").append(res[0]).append(" bytes)\n");
|
||||||
|
sb.append("Contains: ").append(res[1]).append(" files, ").append(res[2]).append(" directories\n");
|
||||||
|
addAttributes(sb, file);
|
||||||
|
infoTextArea.setText(sb.toString());
|
||||||
|
infoTextArea.setCaretPosition(0);
|
||||||
|
} catch (Exception e) {
|
||||||
|
sb.append("Error calculating stats: ").append(e.getMessage());
|
||||||
|
infoTextArea.setText(sb.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addAttributes(StringBuilder sb, File file) {
|
||||||
|
sb.append("\nAttributes:\n");
|
||||||
|
try {
|
||||||
|
Path path = file.toPath();
|
||||||
|
BasicFileAttributes basic = Files.readAttributes(path, BasicFileAttributes.class);
|
||||||
|
sb.append(" Created: ").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(basic.creationTime().toMillis()))).append("\n");
|
||||||
|
sb.append(" Last access: ").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(basic.lastAccessTime().toMillis()))).append("\n");
|
||||||
|
|
||||||
|
if (Files.getFileStore(path).supportsFileAttributeView("posix")) {
|
||||||
|
PosixFileAttributes posix = Files.readAttributes(path, PosixFileAttributes.class);
|
||||||
|
sb.append(" Owner: ").append(posix.owner().getName()).append("\n");
|
||||||
|
sb.append(" Group: ").append(posix.group().getName()).append("\n");
|
||||||
|
sb.append(" Permissions: ").append(PosixFilePermissions.toString(posix.permissions())).append("\n");
|
||||||
|
} else if (Files.getFileStore(path).supportsFileAttributeView("dos")) {
|
||||||
|
DosFileAttributes dos = Files.readAttributes(path, DosFileAttributes.class);
|
||||||
|
sb.append(" Hidden: ").append(dos.isHidden()).append("\n");
|
||||||
|
sb.append(" ReadOnly: ").append(dos.isReadOnly()).append("\n");
|
||||||
|
sb.append(" System: ").append(dos.isSystem()).append("\n");
|
||||||
|
sb.append(" Archive: ").append(dos.isArchive()).append("\n");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
sb.append(" Error reading attributes: ").append(e.getMessage()).append("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void calculateDirStats(File dir, long[] results) {
|
||||||
|
File[] items = dir.listFiles();
|
||||||
|
if (items != null) {
|
||||||
|
for (File item : items) {
|
||||||
|
if (item.isFile()) {
|
||||||
|
results[0] += item.length();
|
||||||
|
results[1]++;
|
||||||
|
} else {
|
||||||
|
results[2]++;
|
||||||
|
calculateDirStats(item, results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void updateColumnRenderers() {
|
private void updateColumnRenderers() {
|
||||||
int columnCount = tableModel.getColumnCount();
|
int columnCount = tableModel.getColumnCount();
|
||||||
@ -2960,10 +3092,6 @@ public class FilePanelTab extends JPanel {
|
|||||||
return currentDirectory;
|
return currentDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ViewMode getViewMode() {
|
|
||||||
return viewMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileTableModel
|
// FileTableModel
|
||||||
private class FileTableModel extends AbstractTableModel {
|
private class FileTableModel extends AbstractTableModel {
|
||||||
private List<FileItem> items = new ArrayList<>();
|
private List<FileItem> items = new ArrayList<>();
|
||||||
|
|||||||
@ -235,6 +235,13 @@ public class MainWindow extends JFrame {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Add selection listeners for Quick View updates
|
||||||
|
leftPanel.getFileTable().getSelectionModel().addListSelectionListener(e -> {
|
||||||
|
if (!e.getValueIsAdjusting() && activePanel == leftPanel) {
|
||||||
|
updateQuickViewInfo();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
rightPanel.getFileTable().addFocusListener(new FocusAdapter() {
|
rightPanel.getFileTable().addFocusListener(new FocusAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void focusGained(FocusEvent e) {
|
public void focusGained(FocusEvent e) {
|
||||||
@ -255,6 +262,13 @@ public class MainWindow extends JFrame {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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
|
// Click on panel anywhere should request focus to its table
|
||||||
leftPanel.addMouseListener(new MouseAdapter() {
|
leftPanel.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
@ -463,7 +477,6 @@ public class MainWindow extends JFrame {
|
|||||||
|
|
||||||
// Load custom shortcuts from config
|
// Load custom shortcuts from config
|
||||||
List<AppConfig.ToolbarShortcut> shortcuts = config.getToolbarShortcuts();
|
List<AppConfig.ToolbarShortcut> shortcuts = config.getToolbarShortcuts();
|
||||||
int btnSize = config.getToolbarButtonSize();
|
|
||||||
int iconSize = config.getToolbarIconSize();
|
int iconSize = config.getToolbarIconSize();
|
||||||
|
|
||||||
// Group shortcuts: directories will go to the right, others stay on the left
|
// Group shortcuts: directories will go to the right, others stay on the left
|
||||||
@ -1200,6 +1213,11 @@ public class MainWindow extends JFrame {
|
|||||||
}, KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK),
|
}, KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK),
|
||||||
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||||
|
|
||||||
|
// Ctrl+Q - Quick View
|
||||||
|
rootPane.registerKeyboardAction(e -> toggleQuickView(),
|
||||||
|
KeyStroke.getKeyStroke(KeyEvent.VK_Q, InputEvent.CTRL_DOWN_MASK),
|
||||||
|
JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||||
|
|
||||||
// Ctrl+E - Command line history
|
// Ctrl+E - Command line history
|
||||||
rootPane.registerKeyboardAction(e -> showCommandLineHistory(),
|
rootPane.registerKeyboardAction(e -> showCommandLineHistory(),
|
||||||
KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK),
|
KeyStroke.getKeyStroke(KeyEvent.VK_E, InputEvent.CTRL_DOWN_MASK),
|
||||||
@ -1222,6 +1240,24 @@ public class MainWindow extends JFrame {
|
|||||||
/**
|
/**
|
||||||
* Switch between panels
|
* Switch between panels
|
||||||
*/
|
*/
|
||||||
|
private void toggleQuickView() {
|
||||||
|
FilePanel opposite = (activePanel == leftPanel) ? rightPanel : leftPanel;
|
||||||
|
if (opposite.getViewMode() == ViewMode.INFO) {
|
||||||
|
opposite.setViewMode(opposite.getPreviousViewMode(), false);
|
||||||
|
} else {
|
||||||
|
opposite.setViewMode(ViewMode.INFO, false);
|
||||||
|
updateQuickViewInfo();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateQuickViewInfo() {
|
||||||
|
FilePanel opposite = (activePanel == leftPanel) ? rightPanel : leftPanel;
|
||||||
|
if (opposite.getViewMode() == ViewMode.INFO) {
|
||||||
|
FileItem selected = activePanel.getFocusedItem();
|
||||||
|
opposite.setInfoItem(selected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void switchPanels() {
|
private void switchPanels() {
|
||||||
// Determine which panel currently (or recently) has focus by inspecting the focus owner.
|
// Determine which panel currently (or recently) has focus by inspecting the focus owner.
|
||||||
java.awt.Component owner = java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
|
java.awt.Component owner = java.awt.KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
|
||||||
|
|||||||
@ -5,5 +5,6 @@ package cz.kamma.kfmanager.ui;
|
|||||||
*/
|
*/
|
||||||
public enum ViewMode {
|
public enum ViewMode {
|
||||||
FULL, // Full details (name, size, date)
|
FULL, // Full details (name, size, date)
|
||||||
BRIEF // Names only in multiple columns
|
BRIEF, // Names only in multiple columns
|
||||||
|
INFO // Information about selected file from opposite panel
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user