Introduce file kinds

This commit is contained in:
crschnick 2023-05-30 10:08:58 +00:00
parent daa011ffe6
commit 4a21dffdab
25 changed files with 94 additions and 164 deletions

View file

@ -2,6 +2,7 @@ package io.xpipe.app.browser;
import io.xpipe.app.core.AppI18n; import io.xpipe.app.core.AppI18n;
import io.xpipe.app.core.AppWindowHelper; import io.xpipe.app.core.AppWindowHelper;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileSystem; import io.xpipe.core.store.FileSystem;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
@ -11,7 +12,7 @@ import java.util.stream.Collectors;
public class BrowserAlerts { public class BrowserAlerts {
public static boolean showMoveAlert(List<FileSystem.FileEntry> source, FileSystem.FileEntry target) { public static boolean showMoveAlert(List<FileSystem.FileEntry> source, FileSystem.FileEntry target) {
if (source.stream().noneMatch(entry -> entry.isDirectory())) { if (source.stream().noneMatch(entry -> entry.getKind() == FileKind.DIRECTORY)) {
return true; return true;
} }
@ -26,7 +27,7 @@ public class BrowserAlerts {
} }
public static boolean showDeleteAlert(List<FileSystem.FileEntry> source) { public static boolean showDeleteAlert(List<FileSystem.FileEntry> source) {
if (source.stream().noneMatch(entry -> entry.isDirectory())) { if (source.stream().noneMatch(entry -> entry.getKind() == FileKind.DIRECTORY)) {
return true; return true;
} }

View file

@ -3,6 +3,7 @@ package io.xpipe.app.browser;
import io.xpipe.app.browser.icon.DirectoryType; import io.xpipe.app.browser.icon.DirectoryType;
import io.xpipe.app.browser.icon.FileType; import io.xpipe.app.browser.icon.FileType;
import io.xpipe.core.impl.FileNames; import io.xpipe.core.impl.FileNames;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileSystem; import io.xpipe.core.store.FileSystem;
import lombok.Getter; import lombok.Getter;
@ -24,7 +25,7 @@ public class BrowserEntry {
} }
private static FileType fileType(FileSystem.FileEntry rawFileEntry) { private static FileType fileType(FileSystem.FileEntry rawFileEntry) {
if (rawFileEntry.isDirectory()) { if (rawFileEntry.getKind() == FileKind.DIRECTORY) {
return null; return null;
} }
@ -38,7 +39,7 @@ public class BrowserEntry {
} }
private static DirectoryType directoryType(FileSystem.FileEntry rawFileEntry) { private static DirectoryType directoryType(FileSystem.FileEntry rawFileEntry) {
if (!rawFileEntry.isDirectory()) { if (rawFileEntry.getKind() != FileKind.DIRECTORY) {
return null; return null;
} }

View file

@ -15,6 +15,7 @@ import io.xpipe.app.util.HumanReadableFormat;
import io.xpipe.app.util.ThreadHelper; import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.impl.FileNames; import io.xpipe.core.impl.FileNames;
import io.xpipe.core.process.OsType; import io.xpipe.core.process.OsType;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileSystem; import io.xpipe.core.store.FileSystem;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
@ -111,7 +112,7 @@ final class BrowserFileListComp extends SimpleComp {
var syntheticFirst = Comparator.<BrowserEntry, Boolean>comparing(path -> !path.isSynthetic()); var syntheticFirst = Comparator.<BrowserEntry, Boolean>comparing(path -> !path.isSynthetic());
var dirsFirst = Comparator.<BrowserEntry, Boolean>comparing( var dirsFirst = Comparator.<BrowserEntry, Boolean>comparing(
path -> !path.getRawFileEntry().isDirectory()); path -> path.getRawFileEntry().getKind() != FileKind.DIRECTORY);
Comparator<? super BrowserEntry> us = Comparator<? super BrowserEntry> us =
syntheticFirst.thenComparing(dirsFirst).thenComparing(comp); syntheticFirst.thenComparing(dirsFirst).thenComparing(comp);
@ -149,9 +150,9 @@ final class BrowserFileListComp extends SimpleComp {
.getCurrentParentDirectory() .getCurrentParentDirectory()
.getPath())); .getPath()));
// Remove unsuitable selection // Remove unsuitable selection
toSelect.removeIf(browserEntry -> (browserEntry.getRawFileEntry().isDirectory() toSelect.removeIf(browserEntry -> (browserEntry.getRawFileEntry().getKind() == FileKind.DIRECTORY
&& !fileList.getMode().isAcceptsDirectories()) && !fileList.getMode().isAcceptsDirectories())
|| (!browserEntry.getRawFileEntry().isDirectory() || (browserEntry.getRawFileEntry().getKind() != FileKind.DIRECTORY
&& !fileList.getMode().isAcceptsFiles())); && !fileList.getMode().isAcceptsFiles()));
fileList.getSelection().setAll(toSelect); fileList.getSelection().setAll(toSelect);
@ -233,11 +234,11 @@ final class BrowserFileListComp extends SimpleComp {
return event.getButton() == MouseButton.SECONDARY; return event.getButton() == MouseButton.SECONDARY;
} }
if (row.getItem() != null && row.getItem().getRawFileEntry().isDirectory()) { if (row.getItem() != null && row.getItem().getRawFileEntry().getKind() == FileKind.DIRECTORY) {
return event.getButton() == MouseButton.SECONDARY; return event.getButton() == MouseButton.SECONDARY;
} }
if (row.getItem() != null && !row.getItem().getRawFileEntry().isDirectory()) { if (row.getItem() != null && row.getItem().getRawFileEntry().getKind() != FileKind.DIRECTORY) {
return event.getButton() == MouseButton.SECONDARY || event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2; return event.getButton() == MouseButton.SECONDARY || event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2;
} }
@ -261,9 +262,9 @@ final class BrowserFileListComp extends SimpleComp {
row.itemProperty().addListener((observable, oldValue, newValue) -> { row.itemProperty().addListener((observable, oldValue, newValue) -> {
row.pseudoClassStateChanged(EMPTY, newValue == null); row.pseudoClassStateChanged(EMPTY, newValue == null);
row.pseudoClassStateChanged( row.pseudoClassStateChanged(
FILE, newValue != null && !newValue.getRawFileEntry().isDirectory()); FILE, newValue != null && newValue.getRawFileEntry().getKind() != FileKind.DIRECTORY);
row.pseudoClassStateChanged( row.pseudoClassStateChanged(
FOLDER, newValue != null && newValue.getRawFileEntry().isDirectory()); FOLDER, newValue != null && newValue.getRawFileEntry().getKind() == FileKind.DIRECTORY);
}); });
fileList.getDraggedOverDirectory().addListener((observable, oldValue, newValue) -> { fileList.getDraggedOverDirectory().addListener((observable, oldValue, newValue) -> {
@ -455,7 +456,7 @@ final class BrowserFileListComp extends SimpleComp {
: getTableRow().getItem().getRawFileEntry(), : getTableRow().getItem().getRawFileEntry(),
isParentLink)); isParentLink));
var isDirectory = getTableRow().getItem().getRawFileEntry().isDirectory(); var isDirectory = getTableRow().getItem().getRawFileEntry().getKind() == FileKind.DIRECTORY;
pseudoClassStateChanged(FOLDER, isDirectory); pseudoClassStateChanged(FOLDER, isDirectory);
var fileName = isParentLink ? ".." : FileNames.getFileName(fullPath); var fileName = isParentLink ? ".." : FileNames.getFileName(fullPath);
@ -477,7 +478,7 @@ final class BrowserFileListComp extends SimpleComp {
setText(null); setText(null);
} else { } else {
var path = getTableRow().getItem(); var path = getTableRow().getItem();
if (path.getRawFileEntry().isDirectory()) { if (path.getRawFileEntry().getKind() == FileKind.DIRECTORY) {
setText(""); setText("");
} else { } else {
setText(byteCount(fileSize.longValue())); setText(byteCount(fileSize.longValue()));

View file

@ -1,5 +1,6 @@
package io.xpipe.app.browser; package io.xpipe.app.browser;
import io.xpipe.core.store.FileKind;
import javafx.geometry.Point2D; import javafx.geometry.Point2D;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.control.TableView; import javafx.scene.control.TableView;
@ -80,7 +81,7 @@ public class BrowserFileListCompEntry {
// Prevent drag and drops of files into the current directory // Prevent drag and drops of files into the current directory
if (BrowserClipboard.currentDragClipboard if (BrowserClipboard.currentDragClipboard
.getBaseDirectory().getPath() .getBaseDirectory().getPath()
.equals(model.getFileSystemModel().getCurrentDirectory().getPath()) && (item == null || !item.getRawFileEntry().isDirectory())) { .equals(model.getFileSystemModel().getCurrentDirectory().getPath()) && (item == null || item.getRawFileEntry().getKind() != FileKind.DIRECTORY)) {
return false; return false;
} }
@ -100,7 +101,7 @@ public class BrowserFileListCompEntry {
if (event.getGestureSource() == null && event.getDragboard().hasFiles()) { if (event.getGestureSource() == null && event.getDragboard().hasFiles()) {
Dragboard db = event.getDragboard(); Dragboard db = event.getDragboard();
var list = db.getFiles().stream().map(File::toPath).toList(); var list = db.getFiles().stream().map(File::toPath).toList();
var target = item != null && item.getRawFileEntry().isDirectory() var target = item != null && item.getRawFileEntry().getKind() == FileKind.DIRECTORY
? item.getRawFileEntry() ? item.getRawFileEntry()
: model.getFileSystemModel().getCurrentDirectory(); : model.getFileSystemModel().getCurrentDirectory();
model.getFileSystemModel().dropLocalFilesIntoAsync(target, list); model.getFileSystemModel().dropLocalFilesIntoAsync(target, list);
@ -111,7 +112,7 @@ public class BrowserFileListCompEntry {
// Accept drops from inside the app window // Accept drops from inside the app window
if (event.getGestureSource() != null) { if (event.getGestureSource() != null) {
var files = BrowserClipboard.retrieveDrag(event.getDragboard()).getEntries(); var files = BrowserClipboard.retrieveDrag(event.getDragboard()).getEntries();
var target = item != null && item.getRawFileEntry().isDirectory() var target = item != null && item.getRawFileEntry().getKind() == FileKind.DIRECTORY
? item.getRawFileEntry() ? item.getRawFileEntry()
: model.getFileSystemModel().getCurrentDirectory(); : model.getFileSystemModel().getCurrentDirectory();
model.getFileSystemModel().dropFilesIntoAsync(target, files, false); model.getFileSystemModel().dropFilesIntoAsync(target, files, false);
@ -121,7 +122,7 @@ public class BrowserFileListCompEntry {
} }
public void onDragExited(DragEvent event) { public void onDragExited(DragEvent event) {
if (item != null && item.getRawFileEntry().isDirectory()) { if (item != null && item.getRawFileEntry().getKind() == FileKind.DIRECTORY) {
model.getDraggedOverDirectory().setValue(null); model.getDraggedOverDirectory().setValue(null);
} else { } else {
model.getDraggedOverEmpty().setValue(false); model.getDraggedOverEmpty().setValue(false);
@ -151,13 +152,13 @@ public class BrowserFileListCompEntry {
} }
private void acceptDrag(DragEvent event) { private void acceptDrag(DragEvent event) {
model.getDraggedOverEmpty().setValue(item == null || !item.getRawFileEntry().isDirectory()); model.getDraggedOverEmpty().setValue(item == null || item.getRawFileEntry().getKind() != FileKind.DIRECTORY);
model.getDraggedOverDirectory().setValue(item); model.getDraggedOverDirectory().setValue(item);
event.acceptTransferModes(TransferMode.COPY_OR_MOVE); event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
} }
private void handleHoverTimer(DragEvent event) { private void handleHoverTimer(DragEvent event) {
if (item == null || !item.getRawFileEntry().isDirectory()) { if (item == null || item.getRawFileEntry().getKind() != FileKind.DIRECTORY) {
return; return;
} }

View file

@ -3,6 +3,7 @@ package io.xpipe.app.browser;
import io.xpipe.app.fxcomps.util.BindingsHelper; import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.core.impl.FileNames; import io.xpipe.core.impl.FileNames;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileSystem; import io.xpipe.core.store.FileSystem;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
@ -23,7 +24,7 @@ import java.util.stream.Stream;
public final class BrowserFileListModel { public final class BrowserFileListModel {
static final Comparator<BrowserEntry> FILE_TYPE_COMPARATOR = static final Comparator<BrowserEntry> FILE_TYPE_COMPARATOR =
Comparator.comparing(path -> !path.getRawFileEntry().isDirectory()); Comparator.comparing(path -> path.getRawFileEntry().getKind() != FileKind.DIRECTORY);
static final Predicate<BrowserEntry> PREDICATE_ANY = path -> true; static final Predicate<BrowserEntry> PREDICATE_ANY = path -> true;
static final Predicate<BrowserEntry> PREDICATE_NOT_HIDDEN = path -> true; static final Predicate<BrowserEntry> PREDICATE_NOT_HIDDEN = path -> true;
@ -112,12 +113,12 @@ public final class BrowserFileListModel {
} }
public void onDoubleClick(BrowserEntry entry) { public void onDoubleClick(BrowserEntry entry) {
if (!entry.getRawFileEntry().isDirectory() && getMode().equals(BrowserModel.Mode.SINGLE_FILE_CHOOSER)) { if (entry.getRawFileEntry().getKind() != FileKind.DIRECTORY && getMode().equals(BrowserModel.Mode.SINGLE_FILE_CHOOSER)) {
getFileSystemModel().getBrowserModel().finishChooser(); getFileSystemModel().getBrowserModel().finishChooser();
return; return;
} }
if (entry.getRawFileEntry().isDirectory()) { if (entry.getRawFileEntry().getKind() == FileKind.DIRECTORY) {
var dir = fileSystemModel.cd(entry.getRawFileEntry().getPath()); var dir = fileSystemModel.cd(entry.getRawFileEntry().getPath());
if (dir.isPresent()) { if (dir.isPresent()) {
fileSystemModel.cd(dir.get()); fileSystemModel.cd(dir.get());

View file

@ -5,6 +5,7 @@ import io.xpipe.core.impl.FileNames;
import io.xpipe.core.impl.LocalStore; import io.xpipe.core.impl.LocalStore;
import io.xpipe.core.process.OsType; import io.xpipe.core.process.OsType;
import io.xpipe.core.store.ConnectionFileSystem; import io.xpipe.core.store.ConnectionFileSystem;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileSystem; import io.xpipe.core.store.FileSystem;
import java.nio.file.Files; import java.nio.file.Files;
@ -96,11 +97,11 @@ public class FileSystemHelper {
localFileSystem, localFileSystem,
file.toString(), file.toString(),
Files.getLastModifiedTime(file).toInstant(), Files.getLastModifiedTime(file).toInstant(),
Files.isDirectory(file),
Files.isHidden(file), Files.isHidden(file),
Files.isExecutable(file), Files.isExecutable(file),
Files.size(file), Files.size(file),
null null,
Files.isDirectory(file) ? FileKind.DIRECTORY : FileKind.FILE
); );
} }
@ -176,7 +177,7 @@ public class FileSystemHelper {
return; return;
} }
if (source.isDirectory()) { if (source.getKind() == FileKind.DIRECTORY) {
var directoryName = FileNames.getFileName(source.getPath()); var directoryName = FileNames.getFileName(source.getPath());
flatFiles.put(source, directoryName); flatFiles.put(source, directoryName);
@ -197,7 +198,7 @@ public class FileSystemHelper {
throw new IllegalStateException(); throw new IllegalStateException();
} }
if (sourceFile.isDirectory()) { if (sourceFile.getKind() == FileKind.DIRECTORY) {
target.getFileSystem().mkdirs(targetFile); target.getFileSystem().mkdirs(targetFile);
} else { } else {
try (var in = sourceFile.getFileSystem().openInput(sourceFile.getPath()); try (var in = sourceFile.getFileSystem().openInput(sourceFile.getPath());

View file

@ -10,10 +10,7 @@ import io.xpipe.app.util.XPipeDaemon;
import io.xpipe.core.impl.FileNames; import io.xpipe.core.impl.FileNames;
import io.xpipe.core.process.ShellControl; import io.xpipe.core.process.ShellControl;
import io.xpipe.core.process.ShellDialects; import io.xpipe.core.process.ShellDialects;
import io.xpipe.core.store.ConnectionFileSystem; import io.xpipe.core.store.*;
import io.xpipe.core.store.FileSystem;
import io.xpipe.core.store.FileSystemStore;
import io.xpipe.core.store.ShellStore;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.*; import javafx.beans.property.*;
import lombok.Getter; import lombok.Getter;
@ -94,7 +91,7 @@ public final class OpenFileSystemModel {
return null; return null;
} }
return new FileSystem.FileEntry(fileSystem, parent, null, true, false, false, 0, null); return new FileSystem.FileEntry(fileSystem, parent, null, false, false, 0, null, FileKind.DIRECTORY);
} }
public FileSystem.FileEntry getCurrentDirectory() { public FileSystem.FileEntry getCurrentDirectory() {
@ -102,7 +99,7 @@ public final class OpenFileSystemModel {
return null; return null;
} }
return new FileSystem.FileEntry(fileSystem, currentPath.get(), null, true, false, false, 0, null); return new FileSystem.FileEntry(fileSystem, currentPath.get(), null, false, false, 0, null, FileKind.DIRECTORY);
} }
public Optional<String> cd(String path) { public Optional<String> cd(String path) {

View file

@ -2,6 +2,7 @@ package io.xpipe.app.browser.icon;
import io.xpipe.app.core.AppResources; import io.xpipe.app.core.AppResources;
import io.xpipe.core.impl.FileNames; import io.xpipe.core.impl.FileNames;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileSystem; import io.xpipe.core.store.FileSystem;
import lombok.Getter; import lombok.Getter;
@ -94,7 +95,7 @@ public interface DirectoryType {
@Override @Override
public boolean matches(FileSystem.FileEntry entry) { public boolean matches(FileSystem.FileEntry entry) {
if (!entry.isDirectory()) { if (entry.getKind() != FileKind.DIRECTORY) {
return false; return false;
} }

View file

@ -3,6 +3,7 @@ package io.xpipe.app.browser.icon;
import io.xpipe.app.core.AppImages; import io.xpipe.app.core.AppImages;
import io.xpipe.app.core.AppResources; import io.xpipe.app.core.AppResources;
import io.xpipe.app.fxcomps.impl.SvgCache; import io.xpipe.app.fxcomps.impl.SvgCache;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileSystem; import io.xpipe.core.store.FileSystem;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import lombok.Getter; import lombok.Getter;
@ -46,7 +47,7 @@ public class FileIconManager {
loadIfNecessary(); loadIfNecessary();
if (!entry.isDirectory()) { if (entry.getKind() != FileKind.DIRECTORY) {
for (var f : FileType.ALL) { for (var f : FileType.ALL) {
if (f.matches(entry)) { if (f.matches(entry)) {
return getIconPath(f.getIcon()); return getIconPath(f.getIcon());
@ -60,7 +61,7 @@ public class FileIconManager {
} }
} }
return entry.isDirectory() ? (open ? "default_folder_opened.svg" : "default_folder.svg") : "default_file.svg"; return entry.getKind() == FileKind.DIRECTORY ? (open ? "default_folder_opened.svg" : "default_folder.svg") : "default_file.svg";
} }
private static String getIconPath(String name) { private static String getIconPath(String name) {

View file

@ -1,6 +1,7 @@
package io.xpipe.app.browser.icon; package io.xpipe.app.browser.icon;
import io.xpipe.app.core.AppResources; import io.xpipe.app.core.AppResources;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileSystem; import io.xpipe.core.store.FileSystem;
import lombok.Getter; import lombok.Getter;
@ -65,7 +66,7 @@ public interface FileType {
@Override @Override
public boolean matches(FileSystem.FileEntry entry) { public boolean matches(FileSystem.FileEntry entry) {
if (entry.isDirectory()) { if (entry.getKind() == FileKind.DIRECTORY) {
return false; return false;
} }

View file

@ -12,7 +12,6 @@ import javafx.application.Application;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage; import javafx.stage.Stage;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@ -64,12 +63,6 @@ public class App extends Application {
public void setupWindow() { public void setupWindow() {
var content = new AppLayoutComp(); var content = new AppLayoutComp();
content.apply(struc -> {
struc.get().addEventFilter(MouseEvent.MOUSE_CLICKED, event -> {
// AppActionLinkDetector.detectOnFocus();
});
});
var titleBinding = Bindings.createStringBinding( var titleBinding = Bindings.createStringBinding(
() -> { () -> {
var base = String.format( var base = String.format(

View file

@ -9,6 +9,7 @@ import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.DefaultSecretValue; import io.xpipe.app.util.DefaultSecretValue;
import io.xpipe.app.util.FileBridge; import io.xpipe.app.util.FileBridge;
import io.xpipe.app.util.LockedSecretValue; import io.xpipe.app.util.LockedSecretValue;
import io.xpipe.core.impl.LocalStore;
import io.xpipe.core.util.JacksonMapper; import io.xpipe.core.util.JacksonMapper;
public class BaseMode extends OperationMode { public class BaseMode extends OperationMode {
@ -37,6 +38,7 @@ public class BaseMode extends OperationMode {
JacksonMapper.configure(objectMapper -> { JacksonMapper.configure(objectMapper -> {
objectMapper.registerSubtypes(LockedSecretValue.class, DefaultSecretValue.class); objectMapper.registerSubtypes(LockedSecretValue.class, DefaultSecretValue.class);
}); });
LocalStore.init();
AppPrefs.init(); AppPrefs.init();
AppCharsets.init(); AppCharsets.init();
AppCharsetter.init(); AppCharsetter.init();

View file

@ -47,6 +47,8 @@ public class TerminalErrorHandler implements ErrorHandler {
event.clearAttachments(); event.clearAttachments();
handleSecondaryException(event, r); handleSecondaryException(event, r);
return; return;
} else {
PlatformState.setCurrent(PlatformState.RUNNING);
} }
} }
} }

View file

@ -2,131 +2,38 @@ package io.xpipe.core.impl;
import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.core.process.ProcessControlProvider; import io.xpipe.core.process.ProcessControlProvider;
import io.xpipe.core.process.ShellDialects;
import io.xpipe.core.process.ShellControl; import io.xpipe.core.process.ShellControl;
import io.xpipe.core.store.ConnectionFileSystem;
import io.xpipe.core.store.FileSystem; import io.xpipe.core.store.FileSystem;
import io.xpipe.core.store.FileSystemStore;
import io.xpipe.core.store.ShellStore; import io.xpipe.core.store.ShellStore;
import io.xpipe.core.util.JacksonizedValue; import io.xpipe.core.util.JacksonizedValue;
import java.nio.file.Path;
@JsonTypeName("local") @JsonTypeName("local")
public class LocalStore extends JacksonizedValue implements ShellStore { public class LocalStore extends JacksonizedValue implements ShellStore {
private static ShellControl local; private static ShellControl local;
private static FileSystem localFileSystem; private static FileSystem localFileSystem;
public static ShellControl getShell() throws Exception { public static void init() throws Exception {
local = ProcessControlProvider.createLocal(false).start();
localFileSystem = new LocalStore().createFileSystem();
}
public static ShellControl getShell() {
if (local == null) { if (local == null) {
local = ProcessControlProvider.createLocal(false).start(); throw new IllegalStateException("Local shell not initialized yet");
} }
return local; return local;
} }
public static FileSystem getFileSystem() throws Exception { public static FileSystem getFileSystem() {
if (localFileSystem == null) { if (localFileSystem == null) {
localFileSystem = new LocalStore().createFileSystem(); throw new IllegalStateException("Local file system not initialized yet");
} }
return localFileSystem; return localFileSystem;
} }
@Override
public FileSystem createFileSystem() {
return new ConnectionFileSystem(ShellStore.createLocal().control(), LocalStore.this) {
@Override
public FileSystemStore getStore() {
return LocalStore.this;
}
private Path wrap(String file) {
for (var e : System.getenv().entrySet()) {
file = file.replace(
ShellDialects.getPlatformDefault().environmentVariable(e.getKey()),
e.getValue());
}
return Path.of(file);
}
// @Override
// public boolean exists(String file) {
// return Files.exists(wrap(file));
// }
//
// @Override
// public void delete(String file) throws Exception {
// Files.delete(wrap(file));
// }
//
// @Override
// public void copy(String file, String newFile) throws Exception {
// Files.copy(wrap(file), wrap(newFile), StandardCopyOption.REPLACE_EXISTING);
// }
//
// @Override
// public void move(String file, String newFile) throws Exception {
// Files.move(wrap(file), wrap(newFile), StandardCopyOption.REPLACE_EXISTING);
// }
//
// @Override
// public boolean mkdirs(String file) throws Exception {
// try {
// Files.createDirectories(wrap(file));
// return true;
// } catch (Exception ex) {
// return false;
// }
// }
//
// @Override
// public void touch(String file) throws Exception {
// if (exists(file)) {
// return;
// }
//
// Files.createFile(wrap(file));
// }
//
// @Override
// public boolean isDirectory(String file) throws Exception {
// return Files.isDirectory(wrap(file));
// }
//
// @Override
// public Stream<FileEntry> listFiles(String file) throws Exception {
// return Files.list(wrap(file)).map(path -> {
// try {
// var date = Files.getLastModifiedTime(path);
// var size = Files.isDirectory(path) ? 0 : Files.size(path);
// return new FileEntry(
// this,
// path.toString(),
// date.toInstant(),
// Files.isDirectory(path),
// Files.isHidden(path),
// Files.isExecutable(path),
// size);
// } catch (IOException e) {
// throw new UncheckedIOException(e);
// }
// });
// }
//
// @Override
// public List<String> listRoots() throws Exception {
// return StreamSupport.stream(
// FileSystems.getDefault().getRootDirectories().spliterator(), false)
// .map(path -> path.toString())
// .toList();
// }
};
}
@Override @Override
public ShellControl createBasicControl() { public ShellControl createBasicControl() {
return ProcessControlProvider.createLocal(true); return ProcessControlProvider.createLocal(true);

View file

@ -0,0 +1,9 @@
package io.xpipe.core.store;
public enum FileKind {
FILE,
DIRECTORY,
LINK,
OTHER;
}

View file

@ -22,29 +22,31 @@ public interface FileSystem extends Closeable, AutoCloseable {
@NonNull @NonNull
String path; String path;
Instant date; Instant date;
boolean directory;
boolean hidden; boolean hidden;
Boolean executable; Boolean executable;
long size; long size;
String mode; String mode;
@NonNull
FileKind kind;
public FileEntry( public FileEntry(
@NonNull FileSystem fileSystem, @NonNull String path, Instant date, boolean directory, boolean hidden, Boolean executable, @NonNull FileSystem fileSystem, @NonNull String path, Instant date, boolean hidden, Boolean executable,
long size, long size,
String mode String mode,
@NonNull FileKind kind
) { ) {
this.fileSystem = fileSystem; this.fileSystem = fileSystem;
this.mode = mode; this.mode = mode;
this.path = directory ? FileNames.toDirectory(path) : path; this.kind = kind;
this.path = kind == FileKind.DIRECTORY ? FileNames.toDirectory(path) : path;
this.date = date; this.date = date;
this.directory = directory;
this.hidden = hidden; this.hidden = hidden;
this.executable = executable; this.executable = executable;
this.size = size; this.size = size;
} }
public static FileEntry ofDirectory(FileSystem fileSystem, String path) { public static FileEntry ofDirectory(FileSystem fileSystem, String path) {
return new FileEntry(fileSystem, path, Instant.now(), true, false, false, 0, null); return new FileEntry(fileSystem, path, Instant.now(), true, false, 0, null, FileKind.DIRECTORY);
} }
} }
@ -79,7 +81,7 @@ public interface FileSystem extends Closeable, AutoCloseable {
default Stream<FileEntry> listFilesRecursively(String file) throws Exception { default Stream<FileEntry> listFilesRecursively(String file) throws Exception {
return listFiles(file).flatMap(fileEntry -> { return listFiles(file).flatMap(fileEntry -> {
if (!fileEntry.isDirectory()) { if (fileEntry.getKind() != FileKind.DIRECTORY) {
return Stream.of(fileEntry); return Stream.of(fileEntry);
} }

View file

@ -6,6 +6,7 @@ import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.core.process.OsType; import io.xpipe.core.process.OsType;
import io.xpipe.core.process.ShellControl; import io.xpipe.core.process.ShellControl;
import io.xpipe.core.process.ShellDialect; import io.xpipe.core.process.ShellDialect;
import io.xpipe.core.store.FileKind;
import java.util.List; import java.util.List;
@ -19,21 +20,21 @@ public class BrowseInNativeManagerAction implements LeafAction {
var e = entry.getRawFileEntry().getPath(); var e = entry.getRawFileEntry().getPath();
switch (OsType.getLocal()) { switch (OsType.getLocal()) {
case OsType.Windows windows -> { case OsType.Windows windows -> {
if (entry.getRawFileEntry().isDirectory()) { if (entry.getRawFileEntry().getKind() == FileKind.DIRECTORY) {
sc.executeSimpleCommand("explorer " + d.fileArgument(e)); sc.executeSimpleCommand("explorer " + d.fileArgument(e));
} else { } else {
sc.executeSimpleCommand("explorer /select," + d.fileArgument(e)); sc.executeSimpleCommand("explorer /select," + d.fileArgument(e));
} }
} }
case OsType.Linux linux -> { case OsType.Linux linux -> {
var action = entry.getRawFileEntry().isDirectory() ? "org.freedesktop.FileManager1.ShowFolders" : "org.freedesktop.FileManager1.ShowItems"; var action = entry.getRawFileEntry().getKind() == FileKind.DIRECTORY ? "org.freedesktop.FileManager1.ShowFolders" : "org.freedesktop.FileManager1.ShowItems";
var dbus = String.format(""" var dbus = String.format("""
dbus-send --session --print-reply --dest=org.freedesktop.FileManager1 --type=method_call /org/freedesktop/FileManager1 %s array:string:"file://%s" string:"" dbus-send --session --print-reply --dest=org.freedesktop.FileManager1 --type=method_call /org/freedesktop/FileManager1 %s array:string:"file://%s" string:""
""", action, entry.getRawFileEntry().getPath()); """, action, entry.getRawFileEntry().getPath());
sc.executeSimpleCommand(dbus); sc.executeSimpleCommand(dbus);
} }
case OsType.MacOs macOs -> { case OsType.MacOs macOs -> {
sc.executeSimpleCommand("open " + (entry.getRawFileEntry().isDirectory() ? "" : "-R ") sc.executeSimpleCommand("open " + (entry.getRawFileEntry().getKind() == FileKind.DIRECTORY ? "" : "-R ")
+ d.fileArgument(entry.getRawFileEntry().getPath())); + d.fileArgument(entry.getRawFileEntry().getPath()));
} }
} }

View file

@ -5,6 +5,7 @@ import io.xpipe.app.browser.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction; import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.FileOpener; import io.xpipe.app.util.FileOpener;
import io.xpipe.core.store.FileKind;
import javafx.scene.Node; import javafx.scene.Node;
import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.javafx.FontIcon;
@ -31,7 +32,7 @@ public class EditFileAction implements LeafAction {
@Override @Override
public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) { public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
return entries.stream().noneMatch(entry -> entry.getRawFileEntry().isDirectory()); return entries.stream().allMatch(entry -> entry.getRawFileEntry().getKind() == FileKind.FILE);
} }
@Override @Override

View file

@ -3,6 +3,7 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.BrowserEntry; import io.xpipe.app.browser.BrowserEntry;
import io.xpipe.app.browser.OpenFileSystemModel; import io.xpipe.app.browser.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction; import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.core.store.FileKind;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCodeCombination;
@ -30,7 +31,7 @@ public class OpenDirectoryAction implements LeafAction {
@Override @Override
public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) { public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
return entries.size() == 1 && entries.stream().allMatch(entry -> entry.getRawFileEntry().isDirectory()); return entries.size() == 1 && entries.stream().allMatch(entry -> entry.getRawFileEntry().getKind() == FileKind.DIRECTORY);
} }
@Override @Override

View file

@ -3,6 +3,7 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.BrowserEntry; import io.xpipe.app.browser.BrowserEntry;
import io.xpipe.app.browser.OpenFileSystemModel; import io.xpipe.app.browser.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction; import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.core.store.FileKind;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCodeCombination;
@ -30,7 +31,7 @@ public class OpenDirectoryInNewTabAction implements LeafAction {
@Override @Override
public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) { public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
return entries.size() == 1 && entries.stream().allMatch(entry -> entry.getRawFileEntry().isDirectory()); return entries.size() == 1 && entries.stream().allMatch(entry -> entry.getRawFileEntry().getKind() == FileKind.DIRECTORY);
} }
@Override @Override

View file

@ -4,6 +4,7 @@ import io.xpipe.app.browser.BrowserEntry;
import io.xpipe.app.browser.OpenFileSystemModel; import io.xpipe.app.browser.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction; import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.app.util.FileOpener; import io.xpipe.app.util.FileOpener;
import io.xpipe.core.store.FileKind;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCodeCombination;
@ -33,7 +34,7 @@ public class OpenFileDefaultAction implements LeafAction {
@Override @Override
public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) { public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
return entries.stream().noneMatch(entry -> entry.getRawFileEntry().isDirectory()); return entries.stream().allMatch(entry -> entry.getRawFileEntry().getKind() == FileKind.FILE);
} }
@Override @Override

View file

@ -8,6 +8,7 @@ import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.core.process.OsType; import io.xpipe.core.process.OsType;
import io.xpipe.core.process.ShellControl; import io.xpipe.core.process.ShellControl;
import io.xpipe.core.process.ShellDialect; import io.xpipe.core.process.ShellDialect;
import io.xpipe.core.store.FileKind;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCodeCombination;
@ -58,7 +59,7 @@ public class OpenFileWithAction implements LeafAction {
return os.isPresent() return os.isPresent()
&& os.get().getOsType().equals(OsType.WINDOWS) && os.get().getOsType().equals(OsType.WINDOWS)
&& entries.size() == 1 && entries.size() == 1
&& entries.stream().noneMatch(entry -> entry.getRawFileEntry().isDirectory()); && entries.stream().allMatch(entry -> entry.getRawFileEntry().getKind() == FileKind.FILE);
} }
@Override @Override

View file

@ -3,6 +3,7 @@ package io.xpipe.ext.base.browser;
import io.xpipe.app.browser.BrowserEntry; import io.xpipe.app.browser.BrowserEntry;
import io.xpipe.app.browser.OpenFileSystemModel; import io.xpipe.app.browser.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction; import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.core.store.FileKind;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCodeCombination;
@ -46,7 +47,7 @@ public class OpenTerminalAction implements LeafAction {
@Override @Override
public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) { public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
return entries.stream().allMatch(entry -> entry.getRawFileEntry().isDirectory()); return entries.stream().allMatch(entry -> entry.getRawFileEntry().getKind() == FileKind.DIRECTORY);
} }
@Override @Override

View file

@ -4,6 +4,7 @@ import io.xpipe.app.browser.BrowserClipboard;
import io.xpipe.app.browser.BrowserEntry; import io.xpipe.app.browser.BrowserEntry;
import io.xpipe.app.browser.OpenFileSystemModel; import io.xpipe.app.browser.OpenFileSystemModel;
import io.xpipe.app.browser.action.LeafAction; import io.xpipe.app.browser.action.LeafAction;
import io.xpipe.core.store.FileKind;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCodeCombination;
@ -21,7 +22,7 @@ public class PasteAction implements LeafAction {
return; return;
} }
var target = entries.size() == 1 && entries.get(0).getRawFileEntry().isDirectory() ? entries.get(0).getRawFileEntry() : model.getCurrentDirectory(); var target = entries.size() == 1 && entries.get(0).getRawFileEntry().getKind() == FileKind.DIRECTORY ? entries.get(0).getRawFileEntry() : model.getCurrentDirectory();
var files = clipboard.getEntries(); var files = clipboard.getEntries();
model.dropFilesIntoAsync(target, files, true); model.dropFilesIntoAsync(target, files, true);
} }
@ -38,7 +39,7 @@ public class PasteAction implements LeafAction {
@Override @Override
public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) { public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
return entries.size() < 2 && entries.stream().allMatch(entry -> entry.getRawFileEntry().isDirectory()); return entries.size() < 2 && entries.stream().allMatch(entry -> entry.getRawFileEntry().getKind() == FileKind.DIRECTORY);
} }
@Override @Override

View file

@ -5,6 +5,7 @@ import io.xpipe.app.browser.OpenFileSystemModel;
import io.xpipe.app.browser.action.MultiExecuteAction; import io.xpipe.app.browser.action.MultiExecuteAction;
import io.xpipe.core.process.OsType; import io.xpipe.core.process.OsType;
import io.xpipe.core.process.ShellControl; import io.xpipe.core.process.ShellControl;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileSystem; import io.xpipe.core.store.FileSystem;
import javafx.scene.Node; import javafx.scene.Node;
import org.kordamp.ikonli.javafx.FontIcon; import org.kordamp.ikonli.javafx.FontIcon;
@ -14,7 +15,7 @@ import java.util.List;
public class RunAction extends MultiExecuteAction { public class RunAction extends MultiExecuteAction {
private boolean isExecutable(FileSystem.FileEntry e) { private boolean isExecutable(FileSystem.FileEntry e) {
if (e.isDirectory()) { if (e.getKind() != FileKind.FILE) {
return false; return false;
} }