added sorting, some fixes
This commit is contained in:
parent
2f87dd0a75
commit
a67b92b015
@ -59,6 +59,11 @@ public class FilePanelTab extends JPanel {
|
|||||||
private int sortColumn = -1; // 0=name,1=size,2=date
|
private int sortColumn = -1; // 0=name,1=size,2=date
|
||||||
private boolean sortAscending = true;
|
private boolean sortAscending = true;
|
||||||
private cz.kamma.kfmanager.config.AppConfig persistedConfig;
|
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
|
// Track last selection to restore it if focus is requested on empty area
|
||||||
private int lastValidRow = 0;
|
private int lastValidRow = 0;
|
||||||
private int lastValidBriefColumn = 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);
|
add(cardPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
// Status bar
|
// Status bar
|
||||||
@ -1222,7 +1305,20 @@ public class FilePanelTab extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<FileItem> items = (preloadedItems != null) ? preloadedItems : createFileItemList(directory);
|
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) {
|
if (viewMode == ViewMode.BRIEF) {
|
||||||
boolean selectFirst = autoSelectFirst;
|
boolean selectFirst = autoSelectFirst;
|
||||||
@ -1346,6 +1442,8 @@ public class FilePanelTab extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final List<FileItem> itemsToLoad = newItems;
|
final List<FileItem> itemsToLoad = newItems;
|
||||||
|
final boolean contentChanged = !isSameContent(newItems, tableModel.items);
|
||||||
|
|
||||||
loadDirectory(currentDirectory, itemsToLoad, false, requestFocus, () -> {
|
loadDirectory(currentDirectory, itemsToLoad, false, requestFocus, () -> {
|
||||||
// Restore marks and set recentlyChanged flag based on active timestamps
|
// Restore marks and set recentlyChanged flag based on active timestamps
|
||||||
for (FileItem item : tableModel.items) {
|
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(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
if (mode == ViewMode.INFO) {
|
if (mode == ViewMode.INFO) {
|
||||||
cardLayout.show(cardPanel, "INFO");
|
cardLayout.show(cardPanel, "INFO");
|
||||||
|
if (briefSortPanel != null) briefSortPanel.setVisible(false);
|
||||||
} else {
|
} else {
|
||||||
cardLayout.show(cardPanel, "TABLE");
|
cardLayout.show(cardPanel, "TABLE");
|
||||||
tableModel.updateViewMode(mode);
|
tableModel.updateViewMode(mode);
|
||||||
// Switch auto-resize behavior depending on mode so BRIEF can scroll horizontally
|
// Switch auto-resize behavior depending on mode so BRIEF can scroll horizontally
|
||||||
if (mode == ViewMode.BRIEF) {
|
if (mode == ViewMode.BRIEF) {
|
||||||
fileTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
fileTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
||||||
|
if (briefSortPanel != null) {
|
||||||
|
briefSortPanel.setVisible(true);
|
||||||
|
updateSortButtonsDisplay();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
fileTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
|
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
|
// Hide table header in BRIEF mode to save vertical space and match requirements
|
||||||
if (fileTable.getTableHeader() != null) {
|
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).
|
* Sort items in the current table model according to column (FULL mode).
|
||||||
* column: 0=name, 1=size, 2=date
|
* 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) {
|
private void sortItemsByColumn(int column, boolean asc) {
|
||||||
if (tableModel == null || tableModel.items == null) return;
|
if (tableModel == null || tableModel.items == null) return;
|
||||||
java.util.List<FileItem> items = tableModel.items;
|
java.util.List<FileItem> items = tableModel.items;
|
||||||
if (items.isEmpty()) return;
|
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;
|
java.util.Comparator<FileItem> comp;
|
||||||
switch (column) {
|
switch (column) {
|
||||||
case 1: // size
|
case 1: // size
|
||||||
comp = (a, b) -> {
|
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());
|
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;
|
return r;
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
case 2: // date
|
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) -> {
|
comp = (a, b) -> {
|
||||||
int r = Long.compare(a.getModified().getTime(), b.getModified().getTime());
|
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;
|
return r;
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
default: // name
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!asc) comp = comp.reversed();
|
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
|
// Refresh table on EDT
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@ -3163,6 +3322,26 @@ public class FilePanelTab extends JPanel {
|
|||||||
updateColumnRenderers();
|
updateColumnRenderers();
|
||||||
updateColumnWidths();
|
updateColumnWidths();
|
||||||
if (fileTable.getTableHeader() != null) fileTable.getTableHeader().repaint();
|
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) {
|
public void setAppConfig(cz.kamma.kfmanager.config.AppConfig cfg) {
|
||||||
this.persistedConfig = cfg;
|
this.persistedConfig = cfg;
|
||||||
iconCache.clear();
|
iconCache.clear();
|
||||||
// Apply persisted sort if present
|
|
||||||
|
// Apply persisted sort if present - try global.sort.column first, then MultipleSortCriteria
|
||||||
if (cfg != null) {
|
if (cfg != null) {
|
||||||
java.util.List<String> multi = cfg.getMultipleSortCriteria();
|
int col = cfg.getDefaultSortColumn();
|
||||||
if (multi != null && !multi.isEmpty()) {
|
boolean asc = cfg.getDefaultSortAscending();
|
||||||
applyMultipleSortCriteria(multi);
|
|
||||||
} else {
|
if (col >= 0) {
|
||||||
int col = cfg.getDefaultSortColumn();
|
// Use global sort column setting
|
||||||
boolean asc = cfg.getDefaultSortAscending();
|
this.sortColumn = col;
|
||||||
if (col >= 0) {
|
this.sortAscending = asc;
|
||||||
this.sortColumn = col;
|
// If items are already loaded, sort them
|
||||||
this.sortAscending = asc;
|
if (tableModel != null && tableModel.items != null && !tableModel.items.isEmpty()) {
|
||||||
sortItemsByColumn(sortColumn, sortAscending);
|
sortItemsByColumn(sortColumn, sortAscending);
|
||||||
SwingUtilities.invokeLater(() -> {
|
}
|
||||||
tableModel.fireTableDataChanged();
|
} else {
|
||||||
if (fileTable.getTableHeader() != null) fileTable.getTableHeader().repaint();
|
// 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user