From 7be8087b19c08160df2909d5738e3ec8d997fa06 Mon Sep 17 00:00:00 2001 From: crschnick Date: Thu, 9 Feb 2023 21:06:58 +0000 Subject: [PATCH] Migrate all actions, fix various bugs --- .../comp/storage/store/StoreEntryComp.java | 15 ++- .../app/storage/DataStateProviderImpl.java | 8 ++ .../io/xpipe/app/storage/DataStorage.java | 8 +- .../io/xpipe/app/util/TerminalProvider.java | 2 +- app/src/main/java/module-info.java | 3 + .../java/io/xpipe/core/process/OsType.java | 4 +- .../core/process/ProcessControlProvider.java | 23 ++--- .../core/process/ShellProcessControl.java | 12 +-- .../java/io/xpipe/core/process/ShellType.java | 6 +- .../xpipe/core/util/FailableBiFunction.java | 7 ++ .../io/xpipe/core/util/FailableFunction.java | 7 ++ .../ext/base/actions/FileBrowseAction.java | 61 ++++++++---- .../ext/base/actions/FileEditAction.java | 75 +++++++++----- .../ext/base/actions/ShareStoreAction.java | 84 ++++++++++------ .../ext/base/actions/StreamExportAction.java | 98 ++++++++++++------- ext/base/src/main/java/module-info.java | 4 +- .../extension/DataStoreActionProvider.java | 59 ----------- .../io/xpipe/extension/util/ScriptHelper.java | 27 +++++ extension/src/main/java/module-info.java | 2 - 19 files changed, 298 insertions(+), 207 deletions(-) create mode 100644 core/src/main/java/io/xpipe/core/util/FailableBiFunction.java create mode 100644 core/src/main/java/io/xpipe/core/util/FailableFunction.java delete mode 100644 extension/src/main/java/io/xpipe/extension/DataStoreActionProvider.java diff --git a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java index 79ac4e4d7..df2b6f4e8 100644 --- a/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java +++ b/app/src/main/java/io/xpipe/app/comp/storage/store/StoreEntryComp.java @@ -171,13 +171,18 @@ public class StoreEntryComp extends SimpleComp { var button = new IconButtonComp( actionProvider.getIcon(entry.getEntry().getStore().asNeeded()), () -> { ThreadHelper.runFailableAsync(() -> { - var action = actionProvider.createAction(entry.getEntry().getStore().asNeeded()); + var action = actionProvider.createAction( + entry.getEntry().getStore().asNeeded()); action.execute(); }); }); button.apply(new FancyTooltipAugment<>( actionProvider.getName(entry.getEntry().getStore().asNeeded()))); - button.disable(Bindings.not(p.getValue())); + if (!actionProvider.showIfDisabled()) { + button.hide(Bindings.not(p.getValue())); + } else { + button.disable(Bindings.not(p.getValue())); + } list.add(button); } @@ -227,7 +232,8 @@ public class StoreEntryComp extends SimpleComp { var item = new MenuItem(null, new FontIcon(icon)); item.setOnAction(event -> { ThreadHelper.runFailableAsync(() -> { - var action = actionProvider.createAction(entry.getEntry().getStore().asNeeded()); + var action = actionProvider.createAction( + entry.getEntry().getStore().asNeeded()); action.execute(); }); }); @@ -245,7 +251,8 @@ public class StoreEntryComp extends SimpleComp { if (AppPrefs.get().developerMode().getValue()) { var browse = new MenuItem(I18n.get("browse"), new FontIcon("mdi2f-folder-open-outline")); - browse.setOnAction(event -> DesktopHelper.browsePath(entry.getEntry().getDirectory())); + browse.setOnAction( + event -> DesktopHelper.browsePath(entry.getEntry().getDirectory())); contextMenu.getItems().add(browse); } diff --git a/app/src/main/java/io/xpipe/app/storage/DataStateProviderImpl.java b/app/src/main/java/io/xpipe/app/storage/DataStateProviderImpl.java index 6db006fd1..f50b24469 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStateProviderImpl.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStateProviderImpl.java @@ -11,6 +11,10 @@ public class DataStateProviderImpl extends DataStateProvider { @Override public void putState(DataStore store, String key, Object value) { + if (DataStorage.get() == null) { + return; + } + var entry = DataStorage.get().getEntryByStore(store); if (entry.isEmpty()) { return; @@ -22,6 +26,10 @@ public class DataStateProviderImpl extends DataStateProvider { @Override public T getState(DataStore store, String key, Class c, Supplier def) { + if (DataStorage.get() == null) { + return def.get(); + } + var entry = DataStorage.get().getEntryByStore(store); if (entry.isEmpty()) { return def.get(); diff --git a/app/src/main/java/io/xpipe/app/storage/DataStorage.java b/app/src/main/java/io/xpipe/app/storage/DataStorage.java index c4fc7ccc8..26c73c2c7 100644 --- a/app/src/main/java/io/xpipe/app/storage/DataStorage.java +++ b/app/src/main/java/io/xpipe/app/storage/DataStorage.java @@ -74,10 +74,6 @@ public abstract class DataStorage { } public static DataStorage get() { - if (INSTANCE == null) { - throw new IllegalStateException("Not initialized"); - } - return INSTANCE; } @@ -175,6 +171,10 @@ public abstract class DataStorage { } public Optional getCollectionForName(String name) { + if (name == null) { + return Optional.ofNullable(getInternalCollection()); + } + return sourceCollections.stream() .filter(c -> name.equalsIgnoreCase(c.getName())) .findAny(); diff --git a/app/src/main/java/io/xpipe/app/util/TerminalProvider.java b/app/src/main/java/io/xpipe/app/util/TerminalProvider.java index 517e53952..46b551cb6 100644 --- a/app/src/main/java/io/xpipe/app/util/TerminalProvider.java +++ b/app/src/main/java/io/xpipe/app/util/TerminalProvider.java @@ -13,7 +13,7 @@ public abstract class TerminalProvider { @Override public void init(ModuleLayer layer) { - ServiceLoader.load(layer, TerminalProvider.class).findFirst().orElseThrow(); + INSTANCE = ServiceLoader.load(layer, TerminalProvider.class).findFirst().orElseThrow(); } @Override diff --git a/app/src/main/java/module-info.java b/app/src/main/java/module-info.java index 726b2513d..493c2ffe4 100644 --- a/app/src/main/java/module-info.java +++ b/app/src/main/java/module-info.java @@ -7,12 +7,14 @@ import io.xpipe.app.exchange.cli.*; import io.xpipe.app.issue.EventHandlerImpl; import io.xpipe.app.storage.DataStateProviderImpl; import io.xpipe.app.util.ProxyManagerProviderImpl; +import io.xpipe.app.util.TerminalProvider; import io.xpipe.app.util.XPipeDaemonProvider; import io.xpipe.core.util.DataStateProvider; import io.xpipe.core.util.ProxyManagerProvider; import io.xpipe.extension.Cache; import io.xpipe.extension.I18n; import io.xpipe.extension.event.EventHandler; +import io.xpipe.extension.util.ModuleLayerLoader; import io.xpipe.extension.util.XPipeDaemon; import org.slf4j.spi.SLF4JServiceProvider; @@ -99,6 +101,7 @@ open module io.xpipe.app { uses MessageExchangeImpl; uses io.xpipe.app.util.TerminalProvider; + provides ModuleLayerLoader with TerminalProvider.Loader; provides DataStateProvider with DataStateProviderImpl; provides ProxyManagerProvider with diff --git a/core/src/main/java/io/xpipe/core/process/OsType.java b/core/src/main/java/io/xpipe/core/process/OsType.java index 236beb611..c62ab09e4 100644 --- a/core/src/main/java/io/xpipe/core/process/OsType.java +++ b/core/src/main/java/io/xpipe/core/process/OsType.java @@ -41,7 +41,7 @@ public interface OsType { @Override public String getTempDirectory(ShellProcessControl pc) throws Exception { - return pc.executeStringSimpleCommand(ShellTypes.CMD, ShellTypes.CMD.getPrintVariableCommand("TEMP")); + return pc.executeStringSimpleCommand(pc.getShellType().getPrintEnvironmentVariableCommand("TEMP")); } @Override @@ -52,7 +52,7 @@ public interface OsType { @Override public Map getProperties(ShellProcessControl pc) throws Exception { try (CommandProcessControl c = - pc.subShell(ShellTypes.CMD).command("systeminfo").start()) { + pc.command("systeminfo").start()) { var text = c.readOrThrow(); return PropertiesFormatsParser.parse(text, ":"); } diff --git a/core/src/main/java/io/xpipe/core/process/ProcessControlProvider.java b/core/src/main/java/io/xpipe/core/process/ProcessControlProvider.java index 903ecaa9b..25b916fff 100644 --- a/core/src/main/java/io/xpipe/core/process/ProcessControlProvider.java +++ b/core/src/main/java/io/xpipe/core/process/ProcessControlProvider.java @@ -1,12 +1,12 @@ package io.xpipe.core.process; +import io.xpipe.core.util.FailableBiFunction; +import io.xpipe.core.util.FailableFunction; import lombok.NonNull; import java.util.List; import java.util.Objects; import java.util.ServiceLoader; -import java.util.function.BiFunction; -import java.util.function.Function; public abstract class ProcessControlProvider { @@ -18,21 +18,22 @@ public abstract class ProcessControlProvider { } public static ShellProcessControl createLocal() { - return INSTANCES.stream().map(localProcessControlProvider -> localProcessControlProvider.createLocalProcessControl()).findFirst().orElseThrow(); + return INSTANCES.stream().map(localProcessControlProvider -> localProcessControlProvider.createLocalProcessControl()).filter( + Objects::nonNull).findFirst().orElseThrow(); } public static ShellProcessControl createSub( ShellProcessControl parent, - @NonNull Function commandFunction, - BiFunction terminalCommand) { + @NonNull FailableFunction commandFunction, + FailableBiFunction terminalCommand) { return INSTANCES.stream().map(localProcessControlProvider -> localProcessControlProvider.sub(parent, commandFunction, terminalCommand)).filter( Objects::nonNull).findFirst().orElseThrow(); } public static CommandProcessControl createCommand( ShellProcessControl parent, - @NonNull Function command, - Function terminalCommand) { + @NonNull FailableFunction command, + FailableFunction terminalCommand) { return INSTANCES.stream().map(localProcessControlProvider -> localProcessControlProvider.command(parent, command, terminalCommand)).filter( Objects::nonNull).findFirst().orElseThrow(); } @@ -44,13 +45,13 @@ public abstract class ProcessControlProvider { public abstract ShellProcessControl sub( ShellProcessControl parent, - @NonNull Function commandFunction, - BiFunction terminalCommand); + @NonNull FailableFunction commandFunction, + FailableBiFunction terminalCommand); public abstract CommandProcessControl command( ShellProcessControl parent, - @NonNull Function command, - Function terminalCommand); + @NonNull FailableFunction command, + FailableFunction terminalCommand); public abstract ShellProcessControl createLocalProcessControl(); diff --git a/core/src/main/java/io/xpipe/core/process/ShellProcessControl.java b/core/src/main/java/io/xpipe/core/process/ShellProcessControl.java index 3e789fa80..83e75d5de 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellProcessControl.java +++ b/core/src/main/java/io/xpipe/core/process/ShellProcessControl.java @@ -1,13 +1,13 @@ package io.xpipe.core.process; +import io.xpipe.core.util.FailableBiFunction; +import io.xpipe.core.util.FailableFunction; import io.xpipe.core.util.SecretValue; import lombok.NonNull; import java.io.IOException; import java.util.List; -import java.util.function.BiFunction; import java.util.function.Consumer; -import java.util.function.Function; import java.util.function.Predicate; public interface ShellProcessControl extends ProcessControl { @@ -87,18 +87,18 @@ public interface ShellProcessControl extends ProcessControl { } ShellProcessControl subShell( - @NonNull Function command, - BiFunction terminalCommand); + FailableFunction command, + FailableBiFunction terminalCommand); void executeLine(String command) throws Exception; @Override ShellProcessControl start() throws Exception; - CommandProcessControl command(Function command); + CommandProcessControl command(FailableFunction command); CommandProcessControl command( - Function command, Function terminalCommand); + FailableFunction command, FailableFunction terminalCommand); default CommandProcessControl command(String command) { return command(shellProcessControl -> command); diff --git a/core/src/main/java/io/xpipe/core/process/ShellType.java b/core/src/main/java/io/xpipe/core/process/ShellType.java index 5de37ffc8..8d27ab76a 100644 --- a/core/src/main/java/io/xpipe/core/process/ShellType.java +++ b/core/src/main/java/io/xpipe/core/process/ShellType.java @@ -60,11 +60,9 @@ public interface ShellType { String getEchoCommand(String s, boolean toErrorStream); - default String getPrintVariableCommand(String name) { - return getPrintVariableCommand("", name); - } + String getPrintVariableCommand(String name); - String getPrintVariableCommand(String prefix, String name); + String getPrintExitCodeCommand(String prefix); default String getPrintEnvironmentVariableCommand(String name) { return getPrintVariableCommand(name); diff --git a/core/src/main/java/io/xpipe/core/util/FailableBiFunction.java b/core/src/main/java/io/xpipe/core/util/FailableBiFunction.java new file mode 100644 index 000000000..0fec7f0f9 --- /dev/null +++ b/core/src/main/java/io/xpipe/core/util/FailableBiFunction.java @@ -0,0 +1,7 @@ +package io.xpipe.core.util; + +@FunctionalInterface +public interface FailableBiFunction { + + R apply(T1 var1, T2 var2) throws E; +} diff --git a/core/src/main/java/io/xpipe/core/util/FailableFunction.java b/core/src/main/java/io/xpipe/core/util/FailableFunction.java new file mode 100644 index 000000000..0f453b64b --- /dev/null +++ b/core/src/main/java/io/xpipe/core/util/FailableFunction.java @@ -0,0 +1,7 @@ +package io.xpipe.core.util; + +@FunctionalInterface +public interface FailableFunction { + + R apply(T var1) throws E; +} diff --git a/ext/base/src/main/java/io/xpipe/ext/base/actions/FileBrowseAction.java b/ext/base/src/main/java/io/xpipe/ext/base/actions/FileBrowseAction.java index 5ead7869d..17fd83020 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/actions/FileBrowseAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/actions/FileBrowseAction.java @@ -2,38 +2,61 @@ package io.xpipe.ext.base.actions; import io.xpipe.core.impl.FileStore; import io.xpipe.core.impl.LocalStore; -import io.xpipe.extension.DataStoreActionProvider; import io.xpipe.extension.I18n; +import io.xpipe.extension.util.ActionProvider; import io.xpipe.extension.util.DesktopHelper; import javafx.beans.value.ObservableValue; +import lombok.Value; import java.nio.file.Files; import java.nio.file.Path; -public class FileBrowseAction implements DataStoreActionProvider { +public class FileBrowseAction implements ActionProvider { - @Override - public Class getApplicableClass() { - return FileStore.class; + @Value + static class Action implements ActionProvider.Action { + + FileStore store; + + @Override + public boolean requiresPlatform() { + return false; + } + + @Override + public void execute() throws Exception { + DesktopHelper.browseFileInDirectory(Path.of(store.getFile())); + } } @Override - public boolean isApplicable(FileStore o) throws Exception { - return o.getFileSystem().equals(new LocalStore()) && Files.exists(Path.of(o.getFile())); - } + public DataStoreCallSite getDataStoreCallSite() { + return new DataStoreCallSite() { - @Override - public ObservableValue getName(FileStore store) { - return I18n.observable("base.browseFile"); - } + @Override + public ActionProvider.Action createAction(FileStore store) { + return new Action(store); + } - @Override - public String getIcon(FileStore store) { - return "mdi2f-folder-open-outline"; - } + @Override + public Class getApplicableClass() { + return FileStore.class; + } - @Override - public void execute(FileStore store) throws Exception { - DesktopHelper.browseFileInDirectory(Path.of(store.getFile())); + @Override + public boolean isApplicable(FileStore o) throws Exception { + return o.getFileSystem().equals(new LocalStore()) && Files.exists(Path.of(o.getFile())); + } + + @Override + public ObservableValue getName(FileStore store) { + return I18n.observable("base.browseFile"); + } + + @Override + public String getIcon(FileStore store) { + return "mdi2f-folder-open-outline"; + } + }; } } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/actions/FileEditAction.java b/ext/base/src/main/java/io/xpipe/ext/base/actions/FileEditAction.java index be7c93f83..c55d926e6 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/actions/FileEditAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/actions/FileEditAction.java @@ -4,39 +4,62 @@ import io.xpipe.app.util.ExternalEditor; import io.xpipe.core.impl.FileStore; import io.xpipe.core.impl.LocalStore; import io.xpipe.core.store.DataFlow; -import io.xpipe.extension.DataStoreActionProvider; import io.xpipe.extension.I18n; +import io.xpipe.extension.util.ActionProvider; import javafx.beans.value.ObservableValue; +import lombok.Value; -public class FileEditAction implements DataStoreActionProvider { +public class FileEditAction implements ActionProvider { - @Override - public Class getApplicableClass() { - return FileStore.class; - } + @Value + static class Action implements ActionProvider.Action { - @Override - public boolean isApplicable(FileStore o) throws Exception { - return o.getFlow().equals(DataFlow.INPUT_OUTPUT); - } + FileStore store; - @Override - public ObservableValue getName(FileStore store) { - return I18n.observable("base.editFile"); - } + @Override + public boolean requiresPlatform() { + return false; + } - @Override - public String getIcon(FileStore store) { - return "mdal-edit"; - } - - @Override - public void execute(FileStore store) throws Exception { - if (store.getFileSystem().equals(new LocalStore())) { - ExternalEditor.get().openInEditor(store.getFile()); - } else { - ExternalEditor.get() - .startEditing(store.getFileName(), store.getFileExtension(), store, () -> store.openInput(), () -> store.openOutput()); + @Override + public void execute() throws Exception { + if (store.getFileSystem().equals(new LocalStore())) { + ExternalEditor.get().openInEditor(store.getFile()); + } else { + ExternalEditor.get() + .startEditing(store.getFileName(), store.getFileExtension(), store, () -> store.openInput(), () -> store.openOutput()); + } } } + + @Override + public DataStoreCallSite getDataStoreCallSite() { + return new DataStoreCallSite() { + + @Override + public ActionProvider.Action createAction(FileStore store) { + return new Action(store); + } + + @Override + public Class getApplicableClass() { + return FileStore.class; + } + + @Override + public boolean isApplicable(FileStore o) throws Exception { + return o.getFlow().equals(DataFlow.INPUT_OUTPUT); + } + + @Override + public ObservableValue getName(FileStore store) { + return I18n.observable("base.editFile"); + } + + @Override + public String getIcon(FileStore store) { + return "mdal-edit"; + } + }; + } } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/actions/ShareStoreAction.java b/ext/base/src/main/java/io/xpipe/ext/base/actions/ShareStoreAction.java index d5c610a71..5d195b93b 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/actions/ShareStoreAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/actions/ShareStoreAction.java @@ -3,52 +3,76 @@ package io.xpipe.ext.base.actions; import io.xpipe.app.core.AppActionLinkDetector; import io.xpipe.core.store.DataStore; import io.xpipe.core.util.SecretValue; -import io.xpipe.extension.DataStoreActionProvider; import io.xpipe.extension.DataStoreProviders; import io.xpipe.extension.I18n; +import io.xpipe.extension.util.ActionProvider; import javafx.beans.value.ObservableValue; +import lombok.Value; import java.awt.*; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; -public class ShareStoreAction implements DataStoreActionProvider { +public class ShareStoreAction implements ActionProvider { - @Override - public boolean showIfDisabled() { - return false; + @Value + static class Action implements ActionProvider.Action { + + DataStore store; + + @Override + public boolean requiresPlatform() { + return false; + } + + public static String create(DataStore store) { + return "xpipe://add/store/" + SecretValue.encrypt(store.toString()).getEncryptedValue(); + } + + @Override + public void execute() throws Exception { + var string = create(store); + var selection = new StringSelection(string); + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + AppActionLinkDetector.setLastDetectedAction(string); + clipboard.setContents(selection, selection); + } } @Override - public Class getApplicableClass() { - return DataStore.class; - } + public DataStoreCallSite getDataStoreCallSite() { + return new DataStoreCallSite() { - @Override - public boolean isApplicable(DataStore o) throws Exception { - return DataStoreProviders.byStore(o).isShareable(); - } - @Override - public ObservableValue getName(DataStore store) { - return I18n.observable("base.copyShareLink"); - } + @Override + public boolean showIfDisabled() { + return false; + } - @Override - public String getIcon(DataStore store) { - return "mdi2c-clipboard-list-outline"; - } + @Override + public ActionProvider.Action createAction(DataStore store) { + return new Action(store); + } - public static String create(DataStore store) { - return "xpipe://add/store/" + SecretValue.encrypt(store.toString()).getEncryptedValue(); - } + @Override + public Class getApplicableClass() { + return DataStore.class; + } - @Override - public void execute(DataStore store) throws Exception { - var string = create(store); - var selection = new StringSelection(string); - Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); - AppActionLinkDetector.setLastDetectedAction(string); - clipboard.setContents(selection, selection); + @Override + public boolean isApplicable(DataStore o) throws Exception { + return DataStoreProviders.byStore(o).isShareable(); + } + + @Override + public ObservableValue getName(DataStore store) { + return I18n.observable("base.copyShareLink"); + } + + @Override + public String getIcon(DataStore store) { + return "mdi2c-clipboard-list-outline"; + } + }; } } diff --git a/ext/base/src/main/java/io/xpipe/ext/base/actions/StreamExportAction.java b/ext/base/src/main/java/io/xpipe/ext/base/actions/StreamExportAction.java index 5eef26585..f939acc6e 100644 --- a/ext/base/src/main/java/io/xpipe/ext/base/actions/StreamExportAction.java +++ b/ext/base/src/main/java/io/xpipe/ext/base/actions/StreamExportAction.java @@ -1,58 +1,84 @@ package io.xpipe.ext.base.actions; import io.xpipe.core.store.StreamDataStore; -import io.xpipe.extension.DataStoreActionProvider; import io.xpipe.extension.I18n; import io.xpipe.extension.event.ErrorEvent; +import io.xpipe.extension.util.ActionProvider; import io.xpipe.extension.util.DesktopHelper; import io.xpipe.extension.util.ThreadHelper; import javafx.beans.value.ObservableValue; import javafx.stage.FileChooser; +import lombok.Value; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; -public class StreamExportAction implements DataStoreActionProvider { - @Override - public Class getApplicableClass() { - return StreamDataStore.class; - } +public class StreamExportAction implements ActionProvider { - @Override - public boolean isApplicable(StreamDataStore o) throws Exception { - return o.getFlow() != null && o.getFlow().hasInput(); - } + @Value + static class Action implements ActionProvider.Action { - @Override - public ObservableValue getName(StreamDataStore store) { - return I18n.observable("base.exportStream"); - } + StreamDataStore store; - @Override - public String getIcon(StreamDataStore store) { - return "mdi2f-file-export-outline"; - } - - @Override - public void execute(StreamDataStore store) throws Exception { - FileChooser fileChooser = new FileChooser(); - fileChooser.setTitle(I18n.get("browseFileTitle")); - fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(I18n.get("anyFile"), ".")); - var outputFile = fileChooser.showSaveDialog(null); - if (outputFile == null) { - return; + @Override + public boolean requiresPlatform() { + return false; } - ThreadHelper.runAsync(() -> { - try (InputStream inputStream = store.openInput()) { - try (OutputStream outputStream = Files.newOutputStream(outputFile.toPath())) { - inputStream.transferTo(outputStream); - } - DesktopHelper.browseFileInDirectory(outputFile.toPath()); - } catch (Exception e) { - ErrorEvent.fromThrowable(e).handle(); + @Override + public void execute() throws Exception { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle(I18n.get("browseFileTitle")); + fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter(I18n.get("anyFile"), ".")); + var outputFile = fileChooser.showSaveDialog(null); + if (outputFile == null) { + return; } - }); + + ThreadHelper.runAsync(() -> { + try (InputStream inputStream = store.openInput()) { + try (OutputStream outputStream = Files.newOutputStream(outputFile.toPath())) { + inputStream.transferTo(outputStream); + } + DesktopHelper.browseFileInDirectory(outputFile.toPath()); + } catch (Exception e) { + ErrorEvent.fromThrowable(e).handle(); + } + }); + } + } + + @Override + public DataStoreCallSite getDataStoreCallSite() { + return new DataStoreCallSite() { + + @Override + public Action createAction(StreamDataStore store) { + return new Action(store); + } + + @Override + public boolean isApplicable(StreamDataStore o) throws Exception { + return o.getFlow() != null && o.getFlow().hasInput(); + } + + @Override + public Class getApplicableClass() { + return StreamDataStore.class; + } + + + @Override + public ObservableValue getName(StreamDataStore store) { + return I18n.observable("base.exportStream"); + } + + @Override + public String getIcon(StreamDataStore store) { + return "mdi2f-file-export-outline"; + } + + }; } } diff --git a/ext/base/src/main/java/module-info.java b/ext/base/src/main/java/module-info.java index e2b8f0508..3b8ce660d 100644 --- a/ext/base/src/main/java/module-info.java +++ b/ext/base/src/main/java/module-info.java @@ -2,7 +2,6 @@ import io.xpipe.ext.base.*; import io.xpipe.ext.base.actions.*; import io.xpipe.ext.base.apps.*; import io.xpipe.extension.DataSourceProvider; -import io.xpipe.extension.DataStoreActionProvider; import io.xpipe.extension.DataStoreProvider; import io.xpipe.extension.DataSourceTarget; import io.xpipe.extension.util.ActionProvider; @@ -22,8 +21,7 @@ open module io.xpipe.ext.base { requires static net.synedra.validatorfx; requires static io.xpipe.app; - provides ActionProvider with AddStoreAction; - provides DataStoreActionProvider with + provides ActionProvider with AddStoreAction, StreamExportAction, ShareStoreAction, FileBrowseAction, diff --git a/extension/src/main/java/io/xpipe/extension/DataStoreActionProvider.java b/extension/src/main/java/io/xpipe/extension/DataStoreActionProvider.java deleted file mode 100644 index 21ea4b821..000000000 --- a/extension/src/main/java/io/xpipe/extension/DataStoreActionProvider.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.xpipe.extension; - -import io.xpipe.core.store.DataStore; -import io.xpipe.extension.event.ErrorEvent; -import javafx.beans.value.ObservableValue; -import javafx.scene.layout.Region; - -import java.util.ArrayList; -import java.util.List; -import java.util.ServiceLoader; - -public interface DataStoreActionProvider { - - static List> ALL = new ArrayList<>(); - - public static void init(ModuleLayer layer) { - if (ALL.size() == 0) { - ALL.addAll(ServiceLoader.load(layer, DataStoreActionProvider.class).stream() - .map(p -> (DataStoreActionProvider) p.get()) - .filter(provider -> { - try { - return provider.isActive(); - } catch (Throwable e) { - ErrorEvent.fromThrowable(e).handle(); - return false; - } - }) - .toList()); - } - } - - Class getApplicableClass(); - - default boolean isMajor() { - return false; - } - - default boolean isActive() throws Exception { - return true; - } - - default boolean isApplicable(T o) throws Exception { - return true; - } - - default void applyToRegion(T store, Region region) { - } - - ObservableValue getName(T store); - - String getIcon(T store); - - default void execute(T store) throws Exception { - } - - default boolean showIfDisabled() { - return true; - } -} diff --git a/extension/src/main/java/io/xpipe/extension/util/ScriptHelper.java b/extension/src/main/java/io/xpipe/extension/util/ScriptHelper.java index 3ab59a015..fbd55c96b 100644 --- a/extension/src/main/java/io/xpipe/extension/util/ScriptHelper.java +++ b/extension/src/main/java/io/xpipe/extension/util/ScriptHelper.java @@ -9,6 +9,7 @@ import io.xpipe.core.util.SecretValue; import io.xpipe.extension.event.TrackEvent; import lombok.SneakyThrows; +import java.util.Arrays; import java.util.List; import java.util.Random; @@ -91,6 +92,18 @@ public class ScriptHelper { } "$@" """; + public static String unquote(String input) { + if (input.startsWith("\"") && input.endsWith("\"")) { + return input.substring(1, input.length() - 1); + } + + if (input.startsWith("'") && input.endsWith("'")) { + return input.substring(1, input.length() - 1); + } + + return input; + } + public static String constructOpenWithInitScriptCommand( ShellProcessControl processControl, List init, String toExecuteInShell) { ShellType t = processControl.getShellType(); @@ -98,6 +111,20 @@ public class ScriptHelper { return t.getNormalOpenCommand(); } + if (init.size() == 0) { + var cmd = unquote(toExecuteInShell); + + // Check for special case of the command to be executed just being another shell script + if (cmd.endsWith(".sh") || cmd.endsWith(".bat")) { + return t.executeCommandWithShell(cmd); + } + + // Check for special case of the command being a shell command + if (Arrays.stream(ShellTypes.getAllShellTypes()).anyMatch(shellType -> cmd.equals(shellType.getNormalOpenCommand()))) { + return cmd; + } + } + String nl = t.getNewLine().getNewLineString(); var content = String.join(nl, init) + nl; diff --git a/extension/src/main/java/module-info.java b/extension/src/main/java/module-info.java index 93e7d11bc..3a89582e1 100644 --- a/extension/src/main/java/module-info.java +++ b/extension/src/main/java/module-info.java @@ -1,6 +1,5 @@ import io.xpipe.core.util.ProxyFunction; import io.xpipe.extension.DataSourceProvider; -import io.xpipe.extension.DataStoreActionProvider; import io.xpipe.extension.DataSourceTarget; import io.xpipe.extension.prefs.PrefsProvider; import io.xpipe.extension.util.ActionProvider; @@ -45,7 +44,6 @@ open module io.xpipe.extension { uses DataSourceProvider; uses DataSourceTarget; - uses DataStoreActionProvider; uses io.xpipe.extension.I18n; uses io.xpipe.extension.event.EventHandler; uses io.xpipe.extension.prefs.PrefsProvider;