From 01a317784345cb5aee81a212309e84f4aab6b4a1 Mon Sep 17 00:00:00 2001 From: crschnick Date: Thu, 23 Feb 2023 17:37:24 +0000 Subject: [PATCH] Various file system fixes --- .../io/xpipe/app/browser/FileListComp.java | 41 ++++++---- .../xpipe/app/browser/FileSystemHelper.java | 75 ++++++++----------- .../io/xpipe/app/update/AppInstaller.java | 12 +-- .../java/io/xpipe/core/impl/FileNames.java | 7 +- .../core/process/CommandProcessControl.java | 31 ++------ .../io/xpipe/core/process/ProcessControl.java | 5 ++ .../java/io/xpipe/core/store/FileSystem.java | 18 +++++ 7 files changed, 98 insertions(+), 91 deletions(-) diff --git a/app/src/main/java/io/xpipe/app/browser/FileListComp.java b/app/src/main/java/io/xpipe/app/browser/FileListComp.java index 429cfa9b..9b2375ad 100644 --- a/app/src/main/java/io/xpipe/app/browser/FileListComp.java +++ b/app/src/main/java/io/xpipe/app/browser/FileListComp.java @@ -91,6 +91,8 @@ final class FileListComp extends AnchorPane { table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); filenameCol.minWidthProperty().bind(table.widthProperty().multiply(0.5)); + var draggedOverDirectory = new SimpleBooleanProperty(); + table.setOnKeyPressed(event -> { if (event.isControlDown() && event.getCode().equals(KeyCode.C) @@ -119,10 +121,7 @@ final class FileListComp extends AnchorPane { return; } - var cm = new FileContextMenu( - fileList.getModel(), - row.getItem(), - editing); + var cm = new FileContextMenu(fileList.getModel(), row.getItem(), editing); if (t.getButton() == MouseButton.SECONDARY) { cm.show(row, t.getScreenX(), t.getScreenY()); } @@ -134,12 +133,20 @@ final class FileListComp extends AnchorPane { } }); + draggedOverDirectory.addListener((observable, oldValue, newValue) -> { + row.pseudoClassStateChanged(DRAG, newValue); + }); + row.setOnDragOver(event -> { if (row.equals(event.getGestureSource())) { return; } - row.pseudoClassStateChanged(DRAG, true); + if (row.getItem() == null || !row.getItem().isDirectory()) { + draggedOverDirectory.set(true); + } else { + row.pseudoClassStateChanged(DRAG, true); + } event.acceptTransferModes(TransferMode.ANY); event.consume(); }); @@ -162,12 +169,13 @@ final class FileListComp extends AnchorPane { }); row.setOnDragExited(event -> { - row.pseudoClassStateChanged(DRAG, false); - event.consume(); + if (row.getItem() != null && row.getItem().isDirectory()) { + row.pseudoClassStateChanged(DRAG, false); + } else { + draggedOverDirectory.set(false); + } if (event.getGestureSource() == null && event.getDragboard().hasFiles()) { - row.pseudoClassStateChanged(DRAG, false); - event.consume(); } // if (event.getGestureSource() != null) { @@ -207,28 +215,31 @@ final class FileListComp extends AnchorPane { // }); row.setOnDragDropped(event -> { + draggedOverDirectory.set(false); + // Accept drops from outside the app window if (event.getGestureSource() == null && event.getDragboard().hasFiles()) { - event.setDropCompleted(true); Dragboard db = event.getDragboard(); var list = db.getFiles().stream().map(File::toPath).toList(); - var target = row.getItem() != null + var target = row.getItem() != null && row.getItem().isDirectory() ? row.getItem() : fileList.getModel().getCurrentDirectory(); fileList.getModel().dropLocalFilesIntoAsync(target, list); + event.setDropCompleted(true); + event.consume(); } // Accept drops from inside the app window if (event.getGestureSource() != null) { - event.setDropCompleted(true); - var files = FileBrowserClipboard.retrieveDrag(event.getDragboard()).getEntries(); + var files = FileBrowserClipboard.retrieveDrag(event.getDragboard()) + .getEntries(); var target = row.getItem() != null ? row.getItem() : fileList.getModel().getCurrentDirectory(); fileList.getModel().dropFilesIntoAsync(target, files, false); + event.setDropCompleted(true); + event.consume(); } - - event.consume(); }); return row; diff --git a/app/src/main/java/io/xpipe/app/browser/FileSystemHelper.java b/app/src/main/java/io/xpipe/app/browser/FileSystemHelper.java index 60828886..e6bf7d46 100644 --- a/app/src/main/java/io/xpipe/app/browser/FileSystemHelper.java +++ b/app/src/main/java/io/xpipe/app/browser/FileSystemHelper.java @@ -76,7 +76,7 @@ public class FileSystemHelper { } public static void dropFilesInto( - FileSystem.FileEntry target, List files, boolean explicitCopy) { + FileSystem.FileEntry target, List files, boolean explicitCopy) throws Exception { if (files.size() == 0) { return; } @@ -91,68 +91,59 @@ public class FileSystemHelper { } private static void dropFileAcrossSameFileSystem( - FileSystem.FileEntry target, FileSystem.FileEntry file, boolean explicitCopy) { + FileSystem.FileEntry target, FileSystem.FileEntry source, boolean explicitCopy) throws Exception { // Prevent dropping directory into itself - if (FileNames.startsWith(file.getPath(), target.getPath())) { + if (FileNames.startsWith(source.getPath(), target.getPath())) { return; } - try { - var sourceFile = file.getPath(); - var targetFile = FileNames.join(target.getPath(), FileNames.getFileName(sourceFile)); - if (explicitCopy) { - target.getFileSystem().copy(sourceFile, targetFile); - } else { - target.getFileSystem().move(sourceFile, targetFile); - } - } catch (Exception ex) { - ErrorEvent.fromThrowable(ex).handle(); + var sourceFile = source.getPath(); + var targetFile = FileNames.join(target.getPath(), FileNames.getFileName(sourceFile)); + if (explicitCopy) { + target.getFileSystem().copy(sourceFile, targetFile); + } else { + target.getFileSystem().move(sourceFile, targetFile); } } - private static void dropFileAcrossFileSystems(FileSystem.FileEntry target, FileSystem.FileEntry file) { + private static void dropFileAcrossFileSystems(FileSystem.FileEntry target, FileSystem.FileEntry source) + throws Exception { var flatFiles = new HashMap(); // Prevent dropping directory into itself - if (file.getFileSystem().equals(target.getFileSystem()) - && FileNames.startsWith(file.getPath(), target.getPath())) { + if (source.getFileSystem().equals(target.getFileSystem()) + && FileNames.startsWith(source.getPath(), target.getPath())) { return; } - try { - if (file.isDirectory()) { - flatFiles.put(file, FileNames.getFileName(file.getPath())); - try (var stream = file.getFileSystem().listFilesRecursively(file.getPath())) { - stream.forEach(fileEntry -> { - flatFiles.put(fileEntry, FileNames.relativize(file.getPath(), fileEntry.getPath())); - }); - } - } else { - flatFiles.put(file, FileNames.getFileName(file.getPath())); + if (source.isDirectory()) { + var directoryName = FileNames.getFileName(source.getPath()); + flatFiles.put(source, directoryName); + + var baseRelative = FileNames.toDirectory(FileNames.getParent(source.getPath())); + try (var stream = source.getFileSystem().listFilesRecursively(source.getPath())) { + stream.forEach(fileEntry -> { + flatFiles.put(fileEntry, FileNames.toUnix(FileNames.relativize(baseRelative, fileEntry.getPath()))); + }); } - } catch (Exception ex) { - ErrorEvent.fromThrowable(ex).handle(); - return; + } else { + flatFiles.put(source, FileNames.getFileName(source.getPath())); } for (var e : flatFiles.entrySet()) { var sourceFile = e.getKey(); var targetFile = FileNames.join(target.getPath(), e.getValue()); - try { - if (sourceFile.getFileSystem().equals(target.getFileSystem())) { - throw new IllegalStateException(); - } + if (sourceFile.getFileSystem().equals(target.getFileSystem())) { + throw new IllegalStateException(); + } - if (sourceFile.isDirectory()) { - target.getFileSystem().mkdirs(targetFile); - } else { - try (var in = sourceFile.getFileSystem().openInput(sourceFile.getPath()); - var out = target.getFileSystem().openOutput(targetFile)) { - in.transferTo(out); - } + if (sourceFile.isDirectory()) { + target.getFileSystem().mkdirs(targetFile); + } else { + try (var in = sourceFile.getFileSystem().openInput(sourceFile.getPath()); + var out = target.getFileSystem().openOutput(targetFile)) { + in.transferTo(out); } - } catch (Exception ex) { - ErrorEvent.fromThrowable(ex).handle(); } } } diff --git a/app/src/main/java/io/xpipe/app/update/AppInstaller.java b/app/src/main/java/io/xpipe/app/update/AppInstaller.java index eb961e0c..6e915247 100644 --- a/app/src/main/java/io/xpipe/app/update/AppInstaller.java +++ b/app/src/main/java/io/xpipe/app/update/AppInstaller.java @@ -190,7 +190,7 @@ public class AppInstaller { .start()) { c.discardOrThrow(); } - pc.executeSimpleCommand("xpipe daemon start"); + pc.executeSimpleCommand("xpipe open"); } } @@ -198,7 +198,7 @@ public class AppInstaller { public void installLocal(String file) throws Exception { var command = "set -x\n" + "DEBIAN_FRONTEND=noninteractive sudo apt-get remove -qy xpipe\n" + "DEBIAN_FRONTEND=noninteractive sudo apt-get install -qy \"" + file + "\"\n" - + "xpipe daemon start"; + + "xpipe open"; TerminalHelper.open("X-Pipe Updater", command); } } @@ -218,13 +218,13 @@ public class AppInstaller { .start()) { c.discardOrThrow(); } - pc.executeSimpleCommand("xpipe daemon start"); + pc.executeSimpleCommand("xpipe open"); } } @Override public void installLocal(String file) throws Exception { - var command = "set -x\n" + "sudo rpm -U -v --force \"" + file + "\"\n" + "xpipe daemon start"; + var command = "set -x\n" + "sudo rpm -U -v --force \"" + file + "\"\n" + "xpipe open"; TerminalHelper.open("X-Pipe Updater", command); } } @@ -245,14 +245,14 @@ public class AppInstaller { .start()) { c.discardOrThrow(); } - pc.executeSimpleCommand("xpipe daemon start"); + pc.executeSimpleCommand("xpipe open"); } } @Override public void installLocal(String file) throws Exception { var command = "set -x\n" + "sudo installer -verboseR -allowUntrusted -pkg \"" + file + "\" -target /\n" - + "xpipe daemon start"; + + "xpipe open"; TerminalHelper.open("X-Pipe Updater", command); } } diff --git a/core/src/main/java/io/xpipe/core/impl/FileNames.java b/core/src/main/java/io/xpipe/core/impl/FileNames.java index a27e7449..77b050bc 100644 --- a/core/src/main/java/io/xpipe/core/impl/FileNames.java +++ b/core/src/main/java/io/xpipe/core/impl/FileNames.java @@ -67,10 +67,13 @@ public class FileNames { public static String toUnix(String file) { var joined = String.join("/", split(file)); - return file.startsWith("/") ? "/" + joined : joined; + var prefix = file.startsWith("/") ? "/" : ""; + var suffix = file.endsWith("/") || file.endsWith("\\") ? "/" : ""; + return prefix + joined + suffix; } public static String toWindows(String file) { - return String.join("\\", split(file)); + var suffix = file.endsWith("/") || file.endsWith("\\") ? "\\" : ""; + return String.join("\\", split(file)) + suffix; } } diff --git a/core/src/main/java/io/xpipe/core/process/CommandProcessControl.java b/core/src/main/java/io/xpipe/core/process/CommandProcessControl.java index e56f3379..9b2c4d2b 100644 --- a/core/src/main/java/io/xpipe/core/process/CommandProcessControl.java +++ b/core/src/main/java/io/xpipe/core/process/CommandProcessControl.java @@ -1,9 +1,10 @@ package io.xpipe.core.process; import io.xpipe.core.charsetter.Charsetter; -import lombok.SneakyThrows; -import java.io.*; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; import java.nio.charset.Charset; import java.util.function.Consumer; @@ -20,31 +21,9 @@ public interface CommandProcessControl extends ProcessControl { ShellProcessControl getParent(); - default InputStream startExternalStdout() throws Exception { - start(); - discardErr(); - return new FilterInputStream(getStdout()) { - @Override - @SneakyThrows - public void close() throws IOException { - CommandProcessControl.this.close(); - } - }; - } + InputStream startExternalStdout() throws Exception; - default OutputStream startExternalStdin() throws Exception { - start(); - discardOut(); - discardErr(); - return new FilterOutputStream(getStdin()) { - @Override - @SneakyThrows - public void close() throws IOException { - closeStdin(); - CommandProcessControl.this.close(); - } - }; - } + OutputStream startExternalStdin() throws Exception; public boolean waitFor(); diff --git a/core/src/main/java/io/xpipe/core/process/ProcessControl.java b/core/src/main/java/io/xpipe/core/process/ProcessControl.java index ad4f144e..ec23cd66 100644 --- a/core/src/main/java/io/xpipe/core/process/ProcessControl.java +++ b/core/src/main/java/io/xpipe/core/process/ProcessControl.java @@ -5,9 +5,14 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; +import java.util.concurrent.ExecutorService; public interface ProcessControl extends Closeable, AutoCloseable { + ExecutorService getStdoutReader(); + + ExecutorService getStderrReader(); + ProcessControl sensitive(); String prepareTerminalOpen() throws Exception; diff --git a/core/src/main/java/io/xpipe/core/store/FileSystem.java b/core/src/main/java/io/xpipe/core/store/FileSystem.java index 6347e714..1420a563 100644 --- a/core/src/main/java/io/xpipe/core/store/FileSystem.java +++ b/core/src/main/java/io/xpipe/core/store/FileSystem.java @@ -1,5 +1,6 @@ package io.xpipe.core.store; +import io.xpipe.core.impl.FileNames; import io.xpipe.core.process.ShellProcessControl; import lombok.NonNull; import lombok.Value; @@ -26,6 +27,19 @@ public interface FileSystem extends Closeable, AutoCloseable { boolean hidden; Boolean executable; long size; + + public FileEntry( + @NonNull FileSystem fileSystem, @NonNull String path, @NonNull Instant date, boolean directory, boolean hidden, Boolean executable, + long size + ) { + this.fileSystem = fileSystem; + this.path = directory ? FileNames.toDirectory(path) : path; + this.date = date; + this.directory = directory; + this.hidden = hidden; + this.executable = executable; + this.size = size; + } } Optional getShell(); @@ -54,6 +68,10 @@ public interface FileSystem extends Closeable, AutoCloseable { default Stream listFilesRecursively(String file) throws Exception { return listFiles(file).flatMap(fileEntry -> { + if (!fileEntry.isDirectory()) { + return Stream.of(fileEntry); + } + try { return listFilesRecursively(fileEntry.getPath()); } catch (Exception e) {