More browser fixes

This commit is contained in:
crschnick 2023-02-22 14:14:01 +00:00
parent 7ca4d64e2d
commit 305f8d69e8
14 changed files with 150 additions and 100 deletions

View file

@ -1,8 +1,6 @@
package io.xpipe.app.browser; package io.xpipe.app.browser;
import io.xpipe.app.util.ThreadHelper; import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.FileSystem;
import io.xpipe.core.store.ShellStore; import io.xpipe.core.store.ShellStore;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
@ -18,20 +16,6 @@ public class BrowserModel {
private final ObservableList<OpenFileSystemModel> openFileSystems = FXCollections.observableArrayList(); private final ObservableList<OpenFileSystemModel> openFileSystems = FXCollections.observableArrayList();
private final Property<OpenFileSystemModel> selected = new SimpleObjectProperty<>(); private final Property<OpenFileSystemModel> selected = new SimpleObjectProperty<>();
public OpenFileSystemModel getOpenModelFor(DataStore store) {
return openFileSystems.stream()
.filter(model -> model.getStore().equals(store))
.findFirst()
.orElseThrow();
}
public OpenFileSystemModel getOpenModelFor(FileSystem fileSystem) {
return openFileSystems.stream()
.filter(model -> model.getFileSystem().equals(fileSystem))
.findFirst()
.orElseThrow();
}
public void closeFileSystem(OpenFileSystemModel open) { public void closeFileSystem(OpenFileSystemModel open) {
ThreadHelper.runAsync(() -> { ThreadHelper.runAsync(() -> {
open.closeSync(); open.closeSync();
@ -41,7 +25,7 @@ public class BrowserModel {
public void openFileSystem(ShellStore store) { public void openFileSystem(ShellStore store) {
var found = openFileSystems.stream() var found = openFileSystems.stream()
.filter(fileSystemModel -> fileSystemModel.getStore().equals(store)) .filter(fileSystemModel -> fileSystemModel.getStore().getValue().equals(store))
.findFirst(); .findFirst();
if (found.isPresent()) { if (found.isPresent()) {
selected.setValue(found.get()); selected.setValue(found.get());

View file

@ -2,10 +2,7 @@
package io.xpipe.app.browser; package io.xpipe.app.browser;
import io.xpipe.app.util.ExternalEditor; import io.xpipe.app.util.*;
import io.xpipe.app.util.ScriptHelper;
import io.xpipe.app.util.TerminalHelper;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.process.OsType; import io.xpipe.core.process.OsType;
import io.xpipe.core.process.ShellProcessControl; import io.xpipe.core.process.ShellProcessControl;
import io.xpipe.core.store.FileSystem; import io.xpipe.core.store.FileSystem;
@ -21,11 +18,15 @@ import java.util.List;
final class FileContextMenu extends ContextMenu { final class FileContextMenu extends ContextMenu {
public boolean isScript(FileSystem.FileEntry e) { public boolean isExecutable(FileSystem.FileEntry e) {
if (e.isDirectory()) { if (e.isDirectory()) {
return false; return false;
} }
if (e.getExecutable() != null && e.getExecutable()) {
return true;
}
var shell = e.getFileSystem().getShell(); var shell = e.getFileSystem().getShell();
if (shell.isEmpty()) { if (shell.isEmpty()) {
return false; return false;
@ -33,7 +34,7 @@ final class FileContextMenu extends ContextMenu {
var os = shell.get().getOsType(); var os = shell.get().getOsType();
var ending = FilenameUtils.getExtension(e.getPath()).toLowerCase(); var ending = FilenameUtils.getExtension(e.getPath()).toLowerCase();
if (os.equals(OsType.WINDOWS) && List.of("bat", "ps1", "cmd").contains(ending)) { if (os.equals(OsType.WINDOWS) && List.of("exe", "bat", "ps1", "cmd").contains(ending)) {
return true; return true;
} }
@ -65,7 +66,7 @@ final class FileContextMenu extends ContextMenu {
}); });
getItems().add(terminal); getItems().add(terminal);
} else { } else {
if (isScript(entry)) { if (isExecutable(entry)) {
var execute = new MenuItem("Run in terminal"); var execute = new MenuItem("Run in terminal");
execute.setOnAction(event -> { execute.setOnAction(event -> {
ThreadHelper.runFailableAsync(() -> { ThreadHelper.runFailableAsync(() -> {
@ -91,23 +92,21 @@ final class FileContextMenu extends ContextMenu {
event.consume(); event.consume();
}); });
getItems().add(executeInBackground); getItems().add(executeInBackground);
} } else {
var open = new MenuItem("Open default");
var open = new MenuItem("Open default"); open.setOnAction(event -> {
open.setOnAction(event -> { ThreadHelper.runFailableAsync(() -> {
ThreadHelper.runFailableAsync(() -> { FileOpener.openInDefaultApplication(entry);
ShellProcessControl pc = model.getFileSystem().getShell().orElseThrow(); });
var cmd = "\"" + entry.getPath() + "\""; event.consume();
pc.executeBooleanSimpleCommand(cmd);
}); });
event.consume(); getItems().add(open);
}); }
getItems().add(open);
var edit = new MenuItem("Edit"); var edit = new MenuItem("Edit");
edit.setOnAction(event -> { edit.setOnAction(event -> {
FileOpener.openInTextEditor(entry);
event.consume(); event.consume();
ExternalEditor.get().openInEditor(model.getFileSystem(), entry.getPath());
}); });
getItems().add(edit); getItems().add(edit);
} }

View file

@ -7,7 +7,6 @@ import atlantafx.base.theme.Tweaks;
import io.xpipe.app.comp.base.LazyTextFieldComp; import io.xpipe.app.comp.base.LazyTextFieldComp;
import io.xpipe.app.core.AppResources; import io.xpipe.app.core.AppResources;
import io.xpipe.app.fxcomps.impl.PrettyImageComp; import io.xpipe.app.fxcomps.impl.PrettyImageComp;
import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.fxcomps.util.SimpleChangeListener; import io.xpipe.app.fxcomps.util.SimpleChangeListener;
import io.xpipe.app.util.Containers; import io.xpipe.app.util.Containers;
import io.xpipe.app.util.HumanReadableFormat; import io.xpipe.app.util.HumanReadableFormat;
@ -235,7 +234,11 @@ final class FileListComp extends AnchorPane {
}); });
fileList.getShown().addListener((observable, oldValue, newValue) -> { fileList.getShown().addListener((observable, oldValue, newValue) -> {
BindingsHelper.setContent(table.getItems(), newValue); table.getItems().setAll(newValue);
if (newValue.size() > 0) {
table.scrollTo(0);
}
}); });
return table; return table;

View file

@ -3,7 +3,7 @@
package io.xpipe.app.browser; package io.xpipe.app.browser;
import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.util.ExternalEditor; import io.xpipe.app.util.FileOpener;
import io.xpipe.core.impl.FileNames; import io.xpipe.core.impl.FileNames;
import io.xpipe.core.store.FileSystem; import io.xpipe.core.store.FileSystem;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
@ -73,7 +73,7 @@ final class FileListModel {
if (entry.isDirectory()) { if (entry.isDirectory()) {
model.navigate(entry.getPath(), true); model.navigate(entry.getPath(), true);
} else { } else {
ExternalEditor.get().openInEditor(entry.getFileSystem(), entry.getPath()); FileOpener.openInTextEditor(entry);
} }
} }

View file

@ -54,6 +54,7 @@ public class FileSystemHelper {
Files.getLastModifiedTime(file).toInstant(), Files.getLastModifiedTime(file).toInstant(),
Files.isDirectory(file), Files.isDirectory(file),
Files.isHidden(file), Files.isHidden(file),
Files.isExecutable(file),
Files.size(file)); Files.size(file));
} }

View file

@ -45,7 +45,7 @@ final class OpenFileSystemModel {
} }
public FileSystem.FileEntry getCurrentDirectory() { public FileSystem.FileEntry getCurrentDirectory() {
return new FileSystem.FileEntry(fileSystem, currentPath.get(), Instant.now(), true, false, 0); return new FileSystem.FileEntry(fileSystem, currentPath.get(), Instant.now(), true, false, false, 0);
} }
public void cd(String path) { public void cd(String path) {
@ -75,7 +75,7 @@ final class OpenFileSystemModel {
newList = getFileSystem().listFiles(dir).collect(Collectors.toCollection(ArrayList::new)); newList = getFileSystem().listFiles(dir).collect(Collectors.toCollection(ArrayList::new));
} else { } else {
newList = getFileSystem().listRoots().stream() newList = getFileSystem().listRoots().stream()
.map(s -> new FileSystem.FileEntry(getFileSystem(), s, Instant.now(), true, false, 0)) .map(s -> new FileSystem.FileEntry(getFileSystem(), s, Instant.now(), true, false, false, 0))
.collect(Collectors.toCollection(ArrayList::new)); .collect(Collectors.toCollection(ArrayList::new));
} }
fileList.setAll(newList); fileList.setAll(newList);
@ -180,7 +180,9 @@ final class OpenFileSystemModel {
public void switchAsync(FileSystemStore fileSystem) { public void switchAsync(FileSystemStore fileSystem) {
ThreadHelper.runFailableAsync(() -> { ThreadHelper.runFailableAsync(() -> {
switchSync(fileSystem); BusyProperty.execute(busy, () -> {
switchSync(fileSystem);
});
}); });
} }

View file

@ -8,7 +8,7 @@ import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.UserReportComp; import io.xpipe.app.issue.UserReportComp;
import io.xpipe.app.util.DesktopHelper; import io.xpipe.app.util.DesktopHelper;
import io.xpipe.app.util.DynamicOptionsBuilder; import io.xpipe.app.util.DynamicOptionsBuilder;
import io.xpipe.app.util.ExternalEditor; import io.xpipe.app.util.FileOpener;
import io.xpipe.core.util.XPipeInstallation; import io.xpipe.core.util.XPipeInstallation;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
@ -30,8 +30,7 @@ public class BrowseDirectoryComp extends SimpleComp {
.addComp( .addComp(
"logFile", "logFile",
new ButtonComp(AppI18n.observable("openCurrentLogFile"), () -> { new ButtonComp(AppI18n.observable("openCurrentLogFile"), () -> {
ExternalEditor.get() FileOpener.openInTextEditor(AppLogs.get()
.openInEditor(AppLogs.get()
.getSessionLogsDirectory() .getSessionLogsDirectory()
.resolve("xpipe.log") .resolve("xpipe.log")
.toString()); .toString());

View file

@ -4,7 +4,7 @@ import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.SimpleComp; import io.xpipe.app.fxcomps.SimpleComp;
import io.xpipe.app.fxcomps.impl.IconButtonComp; import io.xpipe.app.fxcomps.impl.IconButtonComp;
import io.xpipe.app.fxcomps.impl.TextAreaComp; import io.xpipe.app.fxcomps.impl.TextAreaComp;
import io.xpipe.app.util.ExternalEditor; import io.xpipe.app.util.FileOpener;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.property.Property; import javafx.beans.property.Property;
import javafx.scene.layout.AnchorPane; import javafx.scene.layout.AnchorPane;
@ -47,8 +47,8 @@ public class IntegratedTextAreaComp extends SimpleComp {
} }
private Region createOpenButton(Region container) { private Region createOpenButton(Region container) {
var button = new IconButtonComp("mdal-edit", () -> ExternalEditor.get() var button = new IconButtonComp("mdal-edit", () -> FileOpener
.startEditing(identifier, fileType, this, value.getValue(), (s) -> { .openString(identifier, fileType, this, value.getValue(), (s) -> {
Platform.runLater(() -> value.setValue(s)); Platform.runLater(() -> value.setValue(s));
})) }))
.createRegion(); .createRegion();

View file

@ -9,7 +9,7 @@ import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.storage.DataStorage; import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.update.AppUpdater; import io.xpipe.app.update.AppUpdater;
import io.xpipe.app.util.ExternalEditor; import io.xpipe.app.util.FileBridge;
import io.xpipe.core.util.JacksonMapper; import io.xpipe.core.util.JacksonMapper;
public class BaseMode extends OperationMode { public class BaseMode extends OperationMode {
@ -40,7 +40,7 @@ public class BaseMode extends OperationMode {
AppCharsetter.init(); AppCharsetter.init();
DataStorage.init(); DataStorage.init();
FileWatchManager.init(); FileWatchManager.init();
ExternalEditor.init(); FileBridge.init();
AppSocketServer.init(); AppSocketServer.init();
AppUpdater.init(); AppUpdater.init();
TrackEvent.info("mode", "Finished base components initialization"); TrackEvent.info("mode", "Finished base components initialization");

View file

@ -4,11 +4,8 @@ import io.xpipe.app.core.FileWatchManager;
import io.xpipe.app.issue.ErrorEvent; import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.TrackEvent; import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs; import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.core.impl.FileNames;
import io.xpipe.core.store.FileSystem;
import lombok.Getter; import lombok.Getter;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.function.FailableSupplier; import org.apache.commons.lang3.function.FailableSupplier;
import java.io.*; import java.io.*;
@ -24,14 +21,14 @@ import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Consumer; import java.util.function.Consumer;
public class ExternalEditor { public class FileBridge {
private static final Path TEMP = private static final Path TEMP =
FileUtils.getTempDirectory().toPath().resolve("xpipe").resolve("editor"); FileUtils.getTempDirectory().toPath().resolve("xpipe").resolve("bridge");
private static ExternalEditor INSTANCE; private static FileBridge INSTANCE;
private final Set<Entry> openEntries = new CopyOnWriteArraySet<>(); private final Set<Entry> openEntries = new CopyOnWriteArraySet<>();
public static ExternalEditor get() { public static FileBridge get() {
return INSTANCE; return INSTANCE;
} }
@ -44,7 +41,7 @@ public class ExternalEditor {
} }
public static void init() { public static void init() {
INSTANCE = new ExternalEditor(); INSTANCE = new FileBridge();
try { try {
FileUtils.forceMkdir(TEMP.toFile()); FileUtils.forceMkdir(TEMP.toFile());
@ -124,13 +121,13 @@ public class ExternalEditor {
return Optional.empty(); return Optional.empty();
} }
public void startEditing(String keyName, String fileType, Object key, String input, Consumer<String> output) { public void openString(String keyName, String fileType, Object key, String input, Consumer<String> output, Consumer<String> consumer) {
if (input == null) { if (input == null) {
input = ""; input = "";
} }
String s = input; String s = input;
startEditing( openIO(
keyName, keyName,
fileType, fileType,
key, key,
@ -141,18 +138,20 @@ public class ExternalEditor {
super.close(); super.close();
output.accept(new String(toByteArray(), StandardCharsets.UTF_8)); output.accept(new String(toByteArray(), StandardCharsets.UTF_8));
} }
}); },
consumer);
} }
public void startEditing( public void openIO(
String keyName, String keyName,
String fileType, String fileType,
Object key, Object key,
FailableSupplier<InputStream, Exception> input, FailableSupplier<InputStream, Exception> input,
FailableSupplier<OutputStream, Exception> output) { FailableSupplier<OutputStream, Exception> output,
Consumer<String> consumer) {
var ext = getForKey(key); var ext = getForKey(key);
if (ext.isPresent()) { if (ext.isPresent()) {
openInEditor(ext.get().file.toString()); consumer.accept(ext.get().file.toString());
return; return;
} }
@ -181,31 +180,7 @@ public class ExternalEditor {
openEntries.add(entry); openEntries.add(entry);
ext = getForKey(key); ext = getForKey(key);
openInEditor(ext.orElseThrow().file.toString()); consumer.accept(ext.orElseThrow().file.toString());
}
public void openInEditor(FileSystem fileSystem, String file) {
var editor = AppPrefs.get().externalEditor().getValue();
if (editor == null || !editor.isSelectable()) {
return;
}
startEditing(FileNames.getFileName(file), FilenameUtils.getExtension(file), file, () -> {
return fileSystem.openInput(file);
}, () -> fileSystem.openOutput(file));
}
public void openInEditor(String file) {
var editor = AppPrefs.get().externalEditor().getValue();
if (editor == null || !editor.isSelectable()) {
return;
}
try {
editor.launch(Path.of(file).toRealPath());
} catch (Exception e) {
ErrorEvent.fromThrowable(e).handle();
}
} }
@Getter @Getter

View file

@ -0,0 +1,84 @@
package io.xpipe.app.util;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.core.impl.FileNames;
import io.xpipe.core.process.OsType;
import io.xpipe.core.store.FileSystem;
import io.xpipe.core.store.ShellStore;
import org.apache.commons.io.FilenameUtils;
import java.nio.file.Path;
import java.util.function.Consumer;
public class FileOpener {
public static void openInDefaultApplication(FileSystem.FileEntry entry) {
var editor = AppPrefs.get().externalEditor().getValue();
if (editor == null || !editor.isSelectable()) {
return;
}
var file = entry.getPath();
FileBridge.get()
.openIO(
FileNames.getFileName(file),
FilenameUtils.getExtension(file),
file,
() -> {
return entry.getFileSystem().openInput(file);
},
() -> entry.getFileSystem().openOutput(file),
s -> openInDefaultApplication(s));
}
public static void openInTextEditor(FileSystem.FileEntry entry) {
var editor = AppPrefs.get().externalEditor().getValue();
if (editor == null || !editor.isSelectable()) {
return;
}
var file = entry.getPath();
FileBridge.get()
.openIO(
FileNames.getFileName(file),
FilenameUtils.getExtension(file),
file,
() -> {
return entry.getFileSystem().openInput(file);
},
() -> entry.getFileSystem().openOutput(file),
FileOpener::openInTextEditor);
}
public static void openInTextEditor(String file) {
var editor = AppPrefs.get().externalEditor().getValue();
if (editor == null) {
return;
}
try {
editor.launch(Path.of(file).toRealPath());
} catch (Exception e) {
ErrorEvent.fromThrowable(e).handle();
}
}
public static void openInDefaultApplication(String file) {
try (var pc = ShellStore.local().create().start()) {
if (pc.getOsType().equals(OsType.WINDOWS)) {
pc.executeSimpleCommand("\"" + file + "\"");
} else if (pc.getOsType().equals(OsType.LINUX)) {
pc.executeSimpleCommand("xdg-open \"" + file + "\"");
} else {
pc.executeSimpleCommand("open \"" + file + "\"");
}
} catch (Exception e) {
ErrorEvent.fromThrowable(e).handle();
}
}
public static void openString(String keyName, String fileType, Object key, String input, Consumer<String> output) {
FileBridge.get().openString(keyName, fileType, key, input, output, file -> openInTextEditor(file));
}
}

View file

@ -15,6 +15,16 @@ import java.util.Random;
public class ScriptHelper { public class ScriptHelper {
public static String createDefaultOpenCommand(ShellProcessControl pc, String file) {
if (pc.getOsType().equals(OsType.WINDOWS)) {
return "\"" + file + "\"";
} else if (pc.getOsType().equals(OsType.LINUX)){
return "xdg-open \"" + file + "\"";
} else {
return "open \"" + file + "\"";
}
}
public static String createDetachCommand(ShellProcessControl pc, String command) { public static String createDetachCommand(ShellProcessControl pc, String command) {
if (pc.getOsType().equals(OsType.WINDOWS)) { if (pc.getOsType().equals(OsType.WINDOWS)) {
return "start \"\" " + command; return "start \"\" " + command;

View file

@ -24,6 +24,7 @@ public interface FileSystem extends Closeable, AutoCloseable {
Instant date; Instant date;
boolean directory; boolean directory;
boolean hidden; boolean hidden;
Boolean executable;
long size; long size;
} }

View file

@ -2,7 +2,7 @@ package io.xpipe.ext.base.actions;
import io.xpipe.app.core.AppI18n; import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.ActionProvider; import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.util.ExternalEditor; import io.xpipe.app.util.FileOpener;
import io.xpipe.core.impl.FileStore; import io.xpipe.core.impl.FileStore;
import io.xpipe.core.impl.LocalStore; import io.xpipe.core.impl.LocalStore;
import io.xpipe.core.store.DataFlow; import io.xpipe.core.store.DataFlow;
@ -24,15 +24,7 @@ public class FileEditAction implements ActionProvider {
@Override @Override
public void execute() throws Exception { public void execute() throws Exception {
if (store.getFileSystem().equals(new LocalStore())) { if (store.getFileSystem().equals(new LocalStore())) {
ExternalEditor.get().openInEditor(store.getFile()); FileOpener.openInTextEditor(store.getFile());
} else {
ExternalEditor.get()
.startEditing(
store.getFileName(),
store.getFileExtension(),
store,
() -> store.openInput(),
() -> store.openOutput());
} }
} }
} }