net drive support for windows
This commit is contained in:
parent
8d7d039a6a
commit
8a8a7a9416
@ -15,7 +15,7 @@ import java.io.InputStreamReader;
|
|||||||
*/
|
*/
|
||||||
public class MainApp {
|
public class MainApp {
|
||||||
|
|
||||||
public static final String APP_VERSION = "1.4.2";
|
public static final String APP_VERSION = "1.4.3";
|
||||||
|
|
||||||
public enum OS {
|
public enum OS {
|
||||||
WINDOWS, LINUX, MACOS, UNKNOWN
|
WINDOWS, LINUX, MACOS, UNKNOWN
|
||||||
|
|||||||
@ -25,32 +25,7 @@ public class DriveSelector extends JDialog {
|
|||||||
((JComponent) getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
((JComponent) getContentPane()).setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
|
||||||
// Get list of available drives
|
// Get list of available drives
|
||||||
java.util.Set<File> driveSet = new java.util.LinkedHashSet<>();
|
java.util.List<File> driveSet = DriveUtils.getAvailableDrives();
|
||||||
|
|
||||||
File[] roots = File.listRoots();
|
|
||||||
if (roots != null) {
|
|
||||||
for (File r : roots) {
|
|
||||||
driveSet.add(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// On Windows, additionally add mapped network drives if missed by listRoots
|
|
||||||
if (cz.kamma.kfmanager.MainApp.CURRENT_OS == cz.kamma.kfmanager.MainApp.OS.WINDOWS) {
|
|
||||||
javax.swing.filechooser.FileSystemView fsv = javax.swing.filechooser.FileSystemView.getFileSystemView();
|
|
||||||
File[] fsvRoots = fsv.getRoots();
|
|
||||||
if (fsvRoots != null) {
|
|
||||||
for (File r : fsvRoots) {
|
|
||||||
File[] devices = fsv.getFiles(r, false);
|
|
||||||
if (devices != null) {
|
|
||||||
for (File d : devices) {
|
|
||||||
if (fsv.isDrive(d) || fsv.isFileSystemRoot(d)) {
|
|
||||||
driveSet.add(d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
List<DriveInfo> drives = new ArrayList<>();
|
List<DriveInfo> drives = new ArrayList<>();
|
||||||
for (File drive : driveSet) {
|
for (File drive : driveSet) {
|
||||||
|
|||||||
349
src/main/java/cz/kamma/kfmanager/ui/DriveUtils.java
Normal file
349
src/main/java/cz/kamma/kfmanager/ui/DriveUtils.java
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
package cz.kamma.kfmanager.ui;
|
||||||
|
|
||||||
|
import cz.kamma.kfmanager.MainApp;
|
||||||
|
|
||||||
|
import javax.swing.filechooser.FileSystemView;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helpers for discovering filesystem roots shown by drive selectors.
|
||||||
|
*/
|
||||||
|
final class DriveUtils {
|
||||||
|
private static final Pattern WINDOWS_DRIVE_PATTERN = Pattern.compile("(?i)(?<![A-Z0-9])([A-Z]:)(?![A-Z0-9])");
|
||||||
|
private static final Pattern WINDOWS_NET_USE_MAPPING_PATTERN =
|
||||||
|
Pattern.compile("(?i)(?<![A-Z0-9])([A-Z]:)\\s+(\\\\\\\\\\S+)");
|
||||||
|
|
||||||
|
private DriveUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<File> getAvailableDrives() {
|
||||||
|
Set<File> drives = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
File[] roots = File.listRoots();
|
||||||
|
if (roots != null) {
|
||||||
|
for (File root : roots) {
|
||||||
|
addDrive(drives, root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log("File.listRoots(): " + describeFiles(roots));
|
||||||
|
|
||||||
|
if (MainApp.CURRENT_OS == MainApp.OS.WINDOWS) {
|
||||||
|
addWindowsFileSystemViewDrives(drives);
|
||||||
|
addWindowsMappedNetworkDrives(drives);
|
||||||
|
}
|
||||||
|
|
||||||
|
log("available drives: " + describeFiles(drives));
|
||||||
|
return new ArrayList<>(drives);
|
||||||
|
}
|
||||||
|
|
||||||
|
static File resolveDriveForLoading(File drive) {
|
||||||
|
log("resolveDriveForLoading input: " + describeFile(drive));
|
||||||
|
if (drive == null || MainApp.CURRENT_OS != MainApp.OS.WINDOWS || drive.isDirectory()) {
|
||||||
|
log("resolveDriveForLoading direct: " + describeFile(drive));
|
||||||
|
return drive;
|
||||||
|
}
|
||||||
|
|
||||||
|
File normalizedDrive = normalizeDriveKey(drive);
|
||||||
|
Map<File, File> mappedDrives = getWindowsMappedNetworkDriveTargets();
|
||||||
|
log("net use mappings: " + describeMappings(mappedDrives));
|
||||||
|
File mappedTarget = mappedDrives.get(normalizedDrive);
|
||||||
|
log("resolved mapped target for " + describeFile(normalizedDrive) + ": " + describeFile(mappedTarget));
|
||||||
|
|
||||||
|
if (mappedDrives.containsKey(normalizedDrive) && reconnectWindowsMappedDrive(normalizedDrive)) {
|
||||||
|
log("resolveDriveForLoading reconnected drive accepted: " + describeFile(normalizedDrive));
|
||||||
|
return normalizedDrive;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mappedTarget != null && mappedTarget.isDirectory()) {
|
||||||
|
log("resolveDriveForLoading mapped target accepted: " + describeFile(mappedTarget));
|
||||||
|
return mappedTarget;
|
||||||
|
}
|
||||||
|
log("resolveDriveForLoading fallback to selected drive: " + describeFile(drive));
|
||||||
|
return drive;
|
||||||
|
}
|
||||||
|
|
||||||
|
static boolean isWindowsMappedOrUncPath(File directory) {
|
||||||
|
if (directory == null || MainApp.CURRENT_OS != MainApp.OS.WINDOWS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = directory.getAbsolutePath();
|
||||||
|
if (path.startsWith("\\\\")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
java.nio.file.Path rootPath = directory.toPath().toAbsolutePath().normalize().getRoot();
|
||||||
|
if (rootPath == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
File root = rootPath.toFile();
|
||||||
|
return getWindowsMappedNetworkDriveTargets().containsKey(normalizeDriveKey(root));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log("mapped path check failed for " + describeFile(directory) + ": " + ex.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static File[] listWindowsDirectoryWithCmd(File directory) {
|
||||||
|
if (directory == null || MainApp.CURRENT_OS != MainApp.OS.WINDOWS) {
|
||||||
|
return new File[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> names = runWindowsDirectoryListCommand(directory);
|
||||||
|
List<File> files = new ArrayList<>();
|
||||||
|
for (String name : names) {
|
||||||
|
if (name == null || name.isBlank() || name.equals(".") || name.equals("..")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
files.add(new File(directory, name));
|
||||||
|
}
|
||||||
|
log("cmd dir entries for " + describeFile(directory) + ": " + describeFiles(files));
|
||||||
|
return files.toArray(new File[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addWindowsFileSystemViewDrives(Set<File> drives) {
|
||||||
|
try {
|
||||||
|
FileSystemView fsv = FileSystemView.getFileSystemView();
|
||||||
|
File[] roots = fsv.getRoots();
|
||||||
|
if (roots != null) {
|
||||||
|
for (File root : roots) {
|
||||||
|
addWindowsShellChildren(drives, fsv, root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addWindowsShellChildren(drives, fsv, fsv.getHomeDirectory());
|
||||||
|
addWindowsShellChildren(drives, fsv, fsv.getDefaultDirectory());
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addWindowsShellChildren(Set<File> drives, FileSystemView fsv, File root) {
|
||||||
|
if (root == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
if (fsv.isDrive(root) || fsv.isFileSystemRoot(root)) {
|
||||||
|
addDrive(drives, root);
|
||||||
|
}
|
||||||
|
File[] files = fsv.getFiles(root, false);
|
||||||
|
if (files == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (File file : files) {
|
||||||
|
if (fsv.isDrive(file) || fsv.isFileSystemRoot(file)) {
|
||||||
|
addDrive(drives, file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addWindowsMappedNetworkDrives(Set<File> drives) {
|
||||||
|
for (File drive : getWindowsMappedNetworkDriveTargets().keySet()) {
|
||||||
|
addDrive(drives, drive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<File, File> getWindowsMappedNetworkDriveTargets() {
|
||||||
|
Map<File, File> mappedDrives = new LinkedHashMap<>();
|
||||||
|
Process process = null;
|
||||||
|
try {
|
||||||
|
process = new ProcessBuilder("cmd.exe", "/c", "net use")
|
||||||
|
.redirectErrorStream(true)
|
||||||
|
.start();
|
||||||
|
try (BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(process.getInputStream(), Charset.defaultCharset()))) {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
log("net use line: " + line);
|
||||||
|
Matcher mappingMatcher = WINDOWS_NET_USE_MAPPING_PATTERN.matcher(line);
|
||||||
|
if (mappingMatcher.find()) {
|
||||||
|
mappedDrives.put(
|
||||||
|
normalizeDriveKey(new File(mappingMatcher.group(1) + "\\")),
|
||||||
|
new File(mappingMatcher.group(2)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher driveMatcher = WINDOWS_DRIVE_PATTERN.matcher(line);
|
||||||
|
while (driveMatcher.find()) {
|
||||||
|
mappedDrives.putIfAbsent(
|
||||||
|
normalizeDriveKey(new File(driveMatcher.group(1) + "\\")),
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (process.waitFor(2, TimeUnit.SECONDS)) {
|
||||||
|
log("net use exit code: " + process.exitValue());
|
||||||
|
} else {
|
||||||
|
log("net use did not finish within timeout");
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
log("net use failed: " + ignore.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (process != null && process.isAlive()) {
|
||||||
|
process.destroyForcibly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mappedDrives;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean reconnectWindowsMappedDrive(File drive) {
|
||||||
|
File normalizedDrive = normalizeDriveKey(drive);
|
||||||
|
if (normalizedDrive == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
log("attempting mapped drive reconnect via dir: " + describeFile(normalizedDrive));
|
||||||
|
int exitCode = runWindowsProbeCommand("cmd.exe", "/c", "dir", normalizedDrive.getPath());
|
||||||
|
log("mapped drive reconnect dir exit code: " + exitCode + ", after: " + describeFile(normalizedDrive));
|
||||||
|
return normalizedDrive.isDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int runWindowsProbeCommand(String... command) {
|
||||||
|
Process process = null;
|
||||||
|
try {
|
||||||
|
process = new ProcessBuilder(command)
|
||||||
|
.redirectErrorStream(true)
|
||||||
|
.start();
|
||||||
|
try (BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(process.getInputStream(), Charset.defaultCharset()))) {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
log("probe line: " + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (process.waitFor(5, TimeUnit.SECONDS)) {
|
||||||
|
return process.exitValue();
|
||||||
|
}
|
||||||
|
log("probe did not finish within timeout");
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log("probe failed: " + ex.getMessage());
|
||||||
|
} finally {
|
||||||
|
if (process != null && process.isAlive()) {
|
||||||
|
process.destroyForcibly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> runWindowsDirectoryListCommand(File directory) {
|
||||||
|
List<String> lines = new ArrayList<>();
|
||||||
|
Process process = null;
|
||||||
|
try {
|
||||||
|
process = new ProcessBuilder("cmd.exe", "/c", "dir", "/a", "/b", directory.getPath())
|
||||||
|
.redirectErrorStream(true)
|
||||||
|
.start();
|
||||||
|
try (BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(process.getInputStream(), Charset.defaultCharset()))) {
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
log("dir list line: " + line);
|
||||||
|
lines.add(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (process.waitFor(5, TimeUnit.SECONDS)) {
|
||||||
|
int exitCode = process.exitValue();
|
||||||
|
log("dir list exit code: " + exitCode);
|
||||||
|
if (exitCode != 0) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log("dir list did not finish within timeout");
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log("dir list failed: " + ex.getMessage());
|
||||||
|
return new ArrayList<>();
|
||||||
|
} finally {
|
||||||
|
if (process != null && process.isAlive()) {
|
||||||
|
process.destroyForcibly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File normalizeDriveKey(File drive) {
|
||||||
|
if (drive == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Matcher matcher = WINDOWS_DRIVE_PATTERN.matcher(drive.getAbsolutePath());
|
||||||
|
if (matcher.find() && matcher.start() == 0) {
|
||||||
|
return new File(matcher.group(1).toUpperCase(Locale.ROOT) + "\\");
|
||||||
|
}
|
||||||
|
return drive.getAbsoluteFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addDrive(Set<File> drives, File drive) {
|
||||||
|
if (drive == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
drives.add(normalizeDriveKey(drive));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void log(String message) {
|
||||||
|
System.err.println("[DRIVE] " + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String describeFile(File file) {
|
||||||
|
if (file == null) {
|
||||||
|
return "<null>";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return file.getPath()
|
||||||
|
+ " [abs=" + file.getAbsolutePath()
|
||||||
|
+ ", exists=" + file.exists()
|
||||||
|
+ ", dir=" + file.isDirectory()
|
||||||
|
+ ", canRead=" + file.canRead()
|
||||||
|
+ "]";
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return file.getPath() + " [describe failed: " + ex.getMessage() + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String describeFiles(File[] files) {
|
||||||
|
if (files == null) {
|
||||||
|
return "<null>";
|
||||||
|
}
|
||||||
|
return describeFiles(Arrays.asList(files));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String describeFiles(Iterable<File> files) {
|
||||||
|
StringBuilder sb = new StringBuilder("[");
|
||||||
|
boolean first = true;
|
||||||
|
for (File file : files) {
|
||||||
|
if (!first) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
sb.append(describeFile(file));
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
return sb.append(']').toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String describeMappings(Map<File, File> mappings) {
|
||||||
|
StringBuilder sb = new StringBuilder("[");
|
||||||
|
boolean first = true;
|
||||||
|
for (Map.Entry<File, File> entry : mappings.entrySet()) {
|
||||||
|
if (!first) {
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
sb.append(describeFile(entry.getKey())).append(" -> ").append(describeFile(entry.getValue()));
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
return sb.append(']').toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -125,20 +125,28 @@ public class FilePanel extends JPanel {
|
|||||||
|
|
||||||
Object selObj = driveCombo.getSelectedItem();
|
Object selObj = driveCombo.getSelectedItem();
|
||||||
if (selObj instanceof File sel) {
|
if (selObj instanceof File sel) {
|
||||||
|
System.err.println("[DRIVE] combo selected: " + describeDriveForLog(sel));
|
||||||
FilePanelTab currentTab = getCurrentTab();
|
FilePanelTab currentTab = getCurrentTab();
|
||||||
if (currentTab != null) {
|
if (currentTab != null) {
|
||||||
File targetDirectory = sel;
|
File targetDirectory = DriveUtils.resolveDriveForLoading(sel);
|
||||||
|
System.err.println("[DRIVE] target after drive resolver: " + describeDriveForLog(targetDirectory));
|
||||||
if (driveSelectionTargetResolver != null) {
|
if (driveSelectionTargetResolver != null) {
|
||||||
try {
|
try {
|
||||||
File resolved = driveSelectionTargetResolver.apply(sel);
|
File resolved = driveSelectionTargetResolver.apply(targetDirectory);
|
||||||
if (resolved != null && resolved.exists() && resolved.isDirectory() && isWithinSelectedDrive(resolved, sel)) {
|
System.err.println("[DRIVE] opposite-panel resolver returned: " + describeDriveForLog(resolved));
|
||||||
|
if (resolved != null && resolved.exists() && resolved.isDirectory() && isWithinSelectedDrive(resolved, targetDirectory)) {
|
||||||
targetDirectory = resolved;
|
targetDirectory = resolved;
|
||||||
|
System.err.println("[DRIVE] opposite-panel resolver accepted: " + describeDriveForLog(targetDirectory));
|
||||||
|
} else {
|
||||||
|
System.err.println("[DRIVE] opposite-panel resolver rejected for selected drive: " + describeDriveForLog(targetDirectory));
|
||||||
}
|
}
|
||||||
} catch (Exception ignore) {
|
} catch (Exception ex) {
|
||||||
|
System.err.println("[DRIVE] opposite-panel resolver failed: " + ex.getMessage());
|
||||||
// fall back to selected drive root
|
// fall back to selected drive root
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
System.err.println("[DRIVE] loading selected drive target: " + describeDriveForLog(targetDirectory));
|
||||||
currentTab.loadDirectory(targetDirectory);
|
currentTab.loadDirectory(targetDirectory);
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
try { currentTab.getFileTable().requestFocusInWindow(); } catch (Exception ignore) {}
|
try { currentTab.getFileTable().requestFocusInWindow(); } catch (Exception ignore) {}
|
||||||
@ -269,6 +277,22 @@ public class FilePanel extends JPanel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String describeDriveForLog(File file) {
|
||||||
|
if (file == null) {
|
||||||
|
return "<null>";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return file.getPath()
|
||||||
|
+ " [abs=" + file.getAbsolutePath()
|
||||||
|
+ ", exists=" + file.exists()
|
||||||
|
+ ", dir=" + file.isDirectory()
|
||||||
|
+ ", canRead=" + file.canRead()
|
||||||
|
+ "]";
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return file.getPath() + " [describe failed: " + ex.getMessage() + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isWithinSelectedDrive(File directory, File selectedDrive) {
|
private boolean isWithinSelectedDrive(File directory, File selectedDrive) {
|
||||||
try {
|
try {
|
||||||
java.nio.file.Path dirPath = directory.toPath().toAbsolutePath().normalize();
|
java.nio.file.Path dirPath = directory.toPath().toAbsolutePath().normalize();
|
||||||
@ -649,33 +673,7 @@ public class FilePanel extends JPanel {
|
|||||||
try {
|
try {
|
||||||
driveCombo.removeAllItems();
|
driveCombo.removeAllItems();
|
||||||
java.util.Set<File> driveSet = new java.util.LinkedHashSet<>();
|
java.util.Set<File> driveSet = new java.util.LinkedHashSet<>();
|
||||||
|
driveSet.addAll(DriveUtils.getAvailableDrives());
|
||||||
// Add standard roots
|
|
||||||
File[] roots = File.listRoots();
|
|
||||||
if (roots != null) {
|
|
||||||
for (File r : roots) {
|
|
||||||
driveSet.add(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// On Windows, additionally add mapped network drives if missed by listRoots
|
|
||||||
if (MainApp.CURRENT_OS == MainApp.OS.WINDOWS) {
|
|
||||||
javax.swing.filechooser.FileSystemView fsv = javax.swing.filechooser.FileSystemView.getFileSystemView();
|
|
||||||
File[] fsvRoots = fsv.getRoots();
|
|
||||||
if (fsvRoots != null) {
|
|
||||||
for (File r : fsvRoots) {
|
|
||||||
// This usually returns "Desktop". We need to look into it or other FSV methods.
|
|
||||||
File[] devices = fsv.getFiles(r, false);
|
|
||||||
if (devices != null) {
|
|
||||||
for (File d : devices) {
|
|
||||||
if (fsv.isDrive(d) || fsv.isFileSystemRoot(d)) {
|
|
||||||
driveSet.add(d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add home directory
|
// Add home directory
|
||||||
driveSet.add(new File(System.getProperty("user.home")));
|
driveSet.add(new File(System.getProperty("user.home")));
|
||||||
|
|||||||
@ -1473,24 +1473,34 @@ public class FilePanelTab extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void loadDirectory(File directory, List<FileItem> preloadedItems, boolean autoSelectFirst, boolean requestFocus, final Runnable postLoadAction) {
|
public void loadDirectory(File directory, List<FileItem> preloadedItems, boolean autoSelectFirst, boolean requestFocus, final Runnable postLoadAction) {
|
||||||
|
File requestedDirectory = directory;
|
||||||
|
System.err.println("[DRIVE] FilePanelTab.loadDirectory requested: " + describeDirectoryForLog(requestedDirectory));
|
||||||
|
|
||||||
// Ensure we load an existing directory - try parents if necessary
|
// Ensure we load an existing directory - try parents if necessary
|
||||||
File dirToLoad = directory;
|
File dirToLoad = directory;
|
||||||
while (dirToLoad != null && !dirToLoad.isDirectory()) {
|
while (dirToLoad != null && !dirToLoad.isDirectory()) {
|
||||||
|
System.err.println("[DRIVE] loadDirectory candidate is not directory, trying parent: " + describeDirectoryForLog(dirToLoad));
|
||||||
dirToLoad = dirToLoad.getParentFile();
|
dirToLoad = dirToLoad.getParentFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dirToLoad == null) {
|
if (dirToLoad == null) {
|
||||||
dirToLoad = new File(System.getProperty("user.home"));
|
dirToLoad = new File(System.getProperty("user.home"));
|
||||||
|
System.err.println("[DRIVE] loadDirectory fell back to user.home: " + describeDirectoryForLog(dirToLoad));
|
||||||
if (!dirToLoad.isDirectory()) {
|
if (!dirToLoad.isDirectory()) {
|
||||||
dirToLoad = new File(File.separator);
|
dirToLoad = new File(File.separator);
|
||||||
|
System.err.println("[DRIVE] loadDirectory fell back to File.separator: " + describeDirectoryForLog(dirToLoad));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
directory = dirToLoad;
|
directory = dirToLoad;
|
||||||
|
if (requestedDirectory != directory) {
|
||||||
|
System.err.println("[DRIVE] loadDirectory final candidate: " + describeDirectoryForLog(directory));
|
||||||
|
}
|
||||||
|
|
||||||
// If we are switching directories, cleanup any previously extracted archive temp dirs
|
// If we are switching directories, cleanup any previously extracted archive temp dirs
|
||||||
cleanupArchiveTempDirIfNeeded(directory);
|
cleanupArchiveTempDirIfNeeded(directory);
|
||||||
|
|
||||||
if (directory == null || !directory.isDirectory()) {
|
if (directory == null || !directory.isDirectory()) {
|
||||||
|
System.err.println("[DRIVE] loadDirectory aborted, final candidate is invalid: " + describeDirectoryForLog(directory));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1847,6 +1857,15 @@ public class FilePanelTab extends JPanel {
|
|||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
File[] files = directory.listFiles();
|
File[] files = directory.listFiles();
|
||||||
|
if (MainApp.CURRENT_OS == MainApp.OS.WINDOWS && DriveUtils.isWindowsMappedOrUncPath(directory)) {
|
||||||
|
File[] cmdFiles = DriveUtils.listWindowsDirectoryWithCmd(directory);
|
||||||
|
System.err.println("[DRIVE] listFiles count=" + (files != null ? files.length : -1)
|
||||||
|
+ ", cmd dir count=" + cmdFiles.length
|
||||||
|
+ " for " + describeDirectoryForLog(directory));
|
||||||
|
files = mergeFileArrays(files, cmdFiles);
|
||||||
|
System.err.println("[DRIVE] merged directory entry count=" + (files != null ? files.length : -1)
|
||||||
|
+ " for " + describeDirectoryForLog(directory));
|
||||||
|
}
|
||||||
List<FileItem> items = new ArrayList<>();
|
List<FileItem> items = new ArrayList<>();
|
||||||
File parent = directory.getParentFile();
|
File parent = directory.getParentFile();
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
@ -1868,6 +1887,28 @@ public class FilePanelTab extends JPanel {
|
|||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private File[] mergeFileArrays(File[] primary, File[] secondary) {
|
||||||
|
if (primary == null || primary.length == 0) {
|
||||||
|
return secondary != null ? secondary : new File[0];
|
||||||
|
}
|
||||||
|
if (secondary == null || secondary.length == 0) {
|
||||||
|
return primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, File> merged = new LinkedHashMap<>();
|
||||||
|
for (File file : primary) {
|
||||||
|
if (file != null) {
|
||||||
|
merged.put(file.getAbsolutePath().toLowerCase(Locale.ROOT), file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (File file : secondary) {
|
||||||
|
if (file != null) {
|
||||||
|
merged.putIfAbsent(file.getAbsolutePath().toLowerCase(Locale.ROOT), file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return merged.values().toArray(new File[0]);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isSameContent(List<FileItem> list1, List<FileItem> list2) {
|
private boolean isSameContent(List<FileItem> list1, List<FileItem> list2) {
|
||||||
if (list1.size() != list2.size()) return false;
|
if (list1.size() != list2.size()) return false;
|
||||||
for (int i = 0; i < list1.size(); i++) {
|
for (int i = 0; i < list1.size(); i++) {
|
||||||
@ -4420,6 +4461,22 @@ public class FilePanelTab extends JPanel {
|
|||||||
public File getCurrentDirectory() {
|
public File getCurrentDirectory() {
|
||||||
return currentDirectory;
|
return currentDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String describeDirectoryForLog(File file) {
|
||||||
|
if (file == null) {
|
||||||
|
return "<null>";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return file.getPath()
|
||||||
|
+ " [abs=" + file.getAbsolutePath()
|
||||||
|
+ ", exists=" + file.exists()
|
||||||
|
+ ", dir=" + file.isDirectory()
|
||||||
|
+ ", canRead=" + file.canRead()
|
||||||
|
+ "]";
|
||||||
|
} catch (Exception ex) {
|
||||||
|
return file.getPath() + " [describe failed: " + ex.getMessage() + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// FileTableModel
|
// FileTableModel
|
||||||
private class FileTableModel extends AbstractTableModel {
|
private class FileTableModel extends AbstractTableModel {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user