added sorting, some fixes

This commit is contained in:
Radek Davidek 2026-03-08 16:17:04 +01:00
parent 2f87dd0a75
commit a67b92b015

View File

@ -59,6 +59,11 @@ public class FilePanelTab extends JPanel {
private int sortColumn = -1; // 0=name,1=size,2=date
private boolean sortAscending = true;
private cz.kamma.kfmanager.config.AppConfig persistedConfig;
// Sorting buttons for BRIEF mode
private JPanel briefSortPanel;
private JButton sortByNameButton;
private JButton sortByDateButton;
private JButton sortBySizeButton;
// Track last selection to restore it if focus is requested on empty area
private int lastValidRow = 0;
private int lastValidBriefColumn = 0;
@ -810,6 +815,84 @@ public class FilePanelTab extends JPanel {
}
});
// Create sorting buttons panel for BRIEF mode with GridLayout for full width
briefSortPanel = new JPanel(new GridLayout(1, 3, 0, 0));
briefSortPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
sortByNameButton = new JButton("Name");
sortByDateButton = new JButton("Date");
sortBySizeButton = new JButton("Size");
// Set buttons to minimal height
sortByNameButton.setPreferredSize(new Dimension(0, 20));
sortByDateButton.setPreferredSize(new Dimension(0, 20));
sortBySizeButton.setPreferredSize(new Dimension(0, 20));
briefSortPanel.add(sortByNameButton);
briefSortPanel.add(sortByDateButton);
briefSortPanel.add(sortBySizeButton);
briefSortPanel.setVisible(false); // Hidden by default, shown only in BRIEF mode
briefSortPanel.setPreferredSize(new Dimension(0, 20));
// Setup sorting button listeners
sortByNameButton.addActionListener(e -> {
if (sortColumn == 0) {
sortAscending = !sortAscending;
} else {
sortColumn = 0;
sortAscending = true;
}
sortItemsByColumn(sortColumn, sortAscending);
tableModel.fireTableDataChanged();
updateStatus();
updateSortButtonsDisplay();
if (persistedConfig != null) {
persistedConfig.setDefaultSortColumn(sortColumn);
persistedConfig.setDefaultSortAscending(sortAscending);
persistedConfig.saveConfig();
}
fileTable.requestFocus();
});
sortByDateButton.addActionListener(e -> {
if (sortColumn == 2) {
sortAscending = !sortAscending;
} else {
sortColumn = 2;
sortAscending = false; // Default: newest first
}
sortItemsByColumn(sortColumn, sortAscending);
tableModel.fireTableDataChanged();
updateStatus();
updateSortButtonsDisplay();
if (persistedConfig != null) {
persistedConfig.setDefaultSortColumn(sortColumn);
persistedConfig.setDefaultSortAscending(sortAscending);
persistedConfig.saveConfig();
}
fileTable.requestFocus();
});
sortBySizeButton.addActionListener(e -> {
if (sortColumn == 1) {
sortAscending = !sortAscending;
} else {
sortColumn = 1;
sortAscending = false; // Default: largest first
}
sortItemsByColumn(sortColumn, sortAscending);
tableModel.fireTableDataChanged();
updateStatus();
updateSortButtonsDisplay();
if (persistedConfig != null) {
persistedConfig.setDefaultSortColumn(sortColumn);
persistedConfig.setDefaultSortAscending(sortAscending);
persistedConfig.saveConfig();
}
fileTable.requestFocus();
});
add(briefSortPanel, BorderLayout.NORTH);
add(cardPanel, BorderLayout.CENTER);
// Status bar
@ -1222,7 +1305,20 @@ public class FilePanelTab extends JPanel {
}
List<FileItem> items = (preloadedItems != null) ? preloadedItems : createFileItemList(directory);
tableModel.setItems(items);
// Only update the model if items have actually changed
boolean itemsChanged = !isSameContent(items, tableModel.items);
if (itemsChanged) {
tableModel.setItems(items);
} else {
// Items haven't changed, but we might still need to apply sort or revalidate
tableModel.items = items;
}
// Apply saved sort settings to newly loaded items
if (sortColumn >= 0) {
sortItemsByColumn(sortColumn, sortAscending);
}
if (viewMode == ViewMode.BRIEF) {
boolean selectFirst = autoSelectFirst;
@ -1346,6 +1442,8 @@ public class FilePanelTab extends JPanel {
}
final List<FileItem> itemsToLoad = newItems;
final boolean contentChanged = !isSameContent(newItems, tableModel.items);
loadDirectory(currentDirectory, itemsToLoad, false, requestFocus, () -> {
// Restore marks and set recentlyChanged flag based on active timestamps
for (FileItem item : tableModel.items) {
@ -1409,7 +1507,9 @@ public class FilePanelTab extends JPanel {
}
}
}
fileTable.repaint();
if (contentChanged) {
fileTable.repaint();
}
});
}
@ -2633,14 +2733,20 @@ public class FilePanelTab extends JPanel {
SwingUtilities.invokeLater(() -> {
if (mode == ViewMode.INFO) {
cardLayout.show(cardPanel, "INFO");
if (briefSortPanel != null) briefSortPanel.setVisible(false);
} else {
cardLayout.show(cardPanel, "TABLE");
tableModel.updateViewMode(mode);
// Switch auto-resize behavior depending on mode so BRIEF can scroll horizontally
if (mode == ViewMode.BRIEF) {
fileTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
if (briefSortPanel != null) {
briefSortPanel.setVisible(true);
updateSortButtonsDisplay();
}
} else {
fileTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
if (briefSortPanel != null) briefSortPanel.setVisible(false);
}
// Hide table header in BRIEF mode to save vertical space and match requirements
if (fileTable.getTableHeader() != null) {
@ -3118,39 +3224,92 @@ public class FilePanelTab extends JPanel {
* Sort items in the current table model according to column (FULL mode).
* column: 0=name, 1=size, 2=date
*/
private void updateSortButtonsDisplay() {
if (sortByNameButton == null || sortByDateButton == null || sortBySizeButton == null) return;
String upArrow = "";
String downArrow = "";
sortByNameButton.setText(sortColumn == 0 ? "Name " + (sortAscending ? upArrow : downArrow) : "Name");
sortByDateButton.setText(sortColumn == 2 ? "Date " + (sortAscending ? upArrow : downArrow) : "Date");
sortBySizeButton.setText(sortColumn == 1 ? "Size " + (sortAscending ? upArrow : downArrow) : "Size");
}
/**
* Remove leading and trailing $ characters for sorting purposes.
*/
private String getCleanNameForSorting(String name) {
if (name == null) return "";
return name.replaceAll("^\\$+|\\$+$", "");
}
private void sortItemsByColumn(int column, boolean asc) {
if (tableModel == null || tableModel.items == null) return;
java.util.List<FileItem> items = tableModel.items;
if (items.isEmpty()) return;
// Remember currently selected item name to restore selection after sort
final String selectedItemName;
FileItem focused = getFocusedItem();
selectedItemName = (focused != null) ? focused.getName() : null;
// Extract and remember the ".." (parent directory) entry if present
FileItem parentDir = null;
if (!items.isEmpty() && items.get(0).getName().equals("..")) {
parentDir = items.remove(0);
}
// Separate directories and files
java.util.List<FileItem> directories = new ArrayList<>();
java.util.List<FileItem> files = new ArrayList<>();
for (FileItem item : items) {
if (item.isDirectory()) {
directories.add(item);
} else {
files.add(item);
}
}
java.util.Comparator<FileItem> comp;
switch (column) {
case 1: // size
comp = (a, b) -> {
boolean da = a.isDirectory(), db = b.isDirectory();
if (da != db) return da ? -1 : 1;
if (da && db) return a.getName().compareToIgnoreCase(b.getName());
int r = Long.compare(a.getSize(), b.getSize());
if (r == 0) r = a.getName().compareToIgnoreCase(b.getName());
if (r == 0) r = getCleanNameForSorting(a.getName()).compareToIgnoreCase(getCleanNameForSorting(b.getName()));
return r;
};
break;
case 2: // date
// Sort by modification time regardless of directory flag so "newest first" places the newest item
// Sort by modification time
comp = (a, b) -> {
int r = Long.compare(a.getModified().getTime(), b.getModified().getTime());
if (r == 0) r = a.getName().compareToIgnoreCase(b.getName());
if (r == 0) r = getCleanNameForSorting(a.getName()).compareToIgnoreCase(getCleanNameForSorting(b.getName()));
return r;
};
break;
default: // name
comp = (a, b) -> compareFileItemsByName(a, b);
comp = (a, b) -> {
String s1 = getCleanNameForSorting(a.getName());
String s2 = getCleanNameForSorting(b.getName());
return s1.compareToIgnoreCase(s2);
};
break;
}
if (!asc) comp = comp.reversed();
items.sort(comp);
// Sort both lists separately
directories.sort(comp);
files.sort(comp);
// Clear and rebuild items list: directories first, then files
items.clear();
if (parentDir != null) {
items.add(parentDir);
}
items.addAll(directories);
items.addAll(files);
// Refresh table on EDT
SwingUtilities.invokeLater(() -> {
@ -3163,6 +3322,26 @@ public class FilePanelTab extends JPanel {
updateColumnRenderers();
updateColumnWidths();
if (fileTable.getTableHeader() != null) fileTable.getTableHeader().repaint();
// Restore selection to the same item (by name) after sorting
if (selectedItemName != null) {
for (int i = 0; i < tableModel.items.size(); i++) {
if (tableModel.items.get(i).getName().equals(selectedItemName)) {
if (viewMode == ViewMode.BRIEF) {
int selRow = i % tableModel.briefRowsPerColumn;
int selCol = i / tableModel.briefRowsPerColumn;
briefCurrentColumn = selCol;
fileTable.setRowSelectionInterval(selRow, selRow);
fileTable.getColumnModel().getSelectionModel().setSelectionInterval(selCol, selCol);
fileTable.scrollRectToVisible(fileTable.getCellRect(selRow, selCol, true));
} else {
fileTable.setRowSelectionInterval(i, i);
fileTable.scrollRectToVisible(fileTable.getCellRect(i, 0, true));
}
break;
}
}
}
});
}
@ -3351,25 +3530,31 @@ public class FilePanelTab extends JPanel {
public void setAppConfig(cz.kamma.kfmanager.config.AppConfig cfg) {
this.persistedConfig = cfg;
iconCache.clear();
// Apply persisted sort if present
// Apply persisted sort if present - try global.sort.column first, then MultipleSortCriteria
if (cfg != null) {
java.util.List<String> multi = cfg.getMultipleSortCriteria();
if (multi != null && !multi.isEmpty()) {
applyMultipleSortCriteria(multi);
} else {
int col = cfg.getDefaultSortColumn();
boolean asc = cfg.getDefaultSortAscending();
if (col >= 0) {
this.sortColumn = col;
this.sortAscending = asc;
int col = cfg.getDefaultSortColumn();
boolean asc = cfg.getDefaultSortAscending();
if (col >= 0) {
// Use global sort column setting
this.sortColumn = col;
this.sortAscending = asc;
// If items are already loaded, sort them
if (tableModel != null && tableModel.items != null && !tableModel.items.isEmpty()) {
sortItemsByColumn(sortColumn, sortAscending);
SwingUtilities.invokeLater(() -> {
tableModel.fireTableDataChanged();
if (fileTable.getTableHeader() != null) fileTable.getTableHeader().repaint();
});
}
} else {
// No global sort setting, try multiple criteria
java.util.List<String> multi = cfg.getMultipleSortCriteria();
if (multi != null && !multi.isEmpty()) {
applyMultipleSortCriteria(multi);
}
}
}
// Update button display immediately (not in invokeLater) so ViewMode changes can see updated state
updateSortButtonsDisplay();
}
/**