Various fixes

This commit is contained in:
crschnick
2024-12-30 23:22:36 +00:00
parent f41779490d
commit 4d150abc00
19 changed files with 361 additions and 310 deletions
@@ -3,7 +3,9 @@ package io.xpipe.app.comp.base;
import io.xpipe.app.comp.SimpleComp;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.util.PlatformThread;
import javafx.beans.property.ListProperty;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
@@ -19,16 +21,17 @@ import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
@Value
@EqualsAndHashCode(callSuper = true)
public class ListSelectorComp<T> extends SimpleComp {
List<T> values;
ObservableList<T> values;
Function<T, String> toString;
ListProperty<T> selected;
Predicate<T> disable;
boolean showAllSelector;
Supplier<Boolean> showAllSelector;
@Override
protected Region createSimple() {
@@ -36,56 +39,62 @@ public class ListSelectorComp<T> extends SimpleComp {
vbox.setSpacing(8);
vbox.getStyleClass().add("list-content");
var cbs = new ArrayList<CheckBox>();
for (var v : values) {
var cb = new CheckBox(null);
if (disable.test(v)) {
cb.setDisable(true);
}
cbs.add(cb);
cb.setAccessibleText(toString.apply(v));
cb.setSelected(selected.contains(v));
cb.selectedProperty().addListener((c, o, n) -> {
if (n) {
selected.add(v);
} else {
selected.remove(v);
}
});
var l = new Label(toString.apply(v), cb);
l.setGraphicTextGap(9);
l.setOnMouseClicked(event -> {
if (disable.test(v)) {
return;
}
cb.setSelected(!cb.isSelected());
event.consume();
});
l.opacityProperty().bind(cb.opacityProperty());
vbox.getChildren().add(l);
}
if (showAllSelector) {
var allSelector = new CheckBox(null);
allSelector.setSelected(
values.stream().filter(t -> !disable.test(t)).count() == selected.size());
allSelector.selectedProperty().addListener((observable, oldValue, newValue) -> {
cbs.forEach(checkBox -> {
if (checkBox.isDisabled()) {
return;
values.subscribe(() -> {
var currentVals = new ArrayList<>(values);
PlatformThread.runLaterIfNeeded(() -> {
vbox.getChildren().clear();
cbs.clear();
for (var v : currentVals) {
var cb = new CheckBox(null);
if (disable.test(v)) {
cb.setDisable(true);
}
cbs.add(cb);
cb.setAccessibleText(toString.apply(v));
cb.setSelected(selected.contains(v));
cb.selectedProperty().addListener((c, o, n) -> {
if (n) {
selected.add(v);
} else {
selected.remove(v);
}
});
var l = new Label(toString.apply(v), cb);
l.setGraphicTextGap(9);
l.setOnMouseClicked(event -> {
if (disable.test(v)) {
return;
}
checkBox.setSelected(newValue);
});
cb.setSelected(!cb.isSelected());
event.consume();
});
l.opacityProperty().bind(cb.opacityProperty());
vbox.getChildren().add(l);
}
if (showAllSelector.get()) {
var allSelector = new CheckBox(null);
allSelector.setSelected(
values.stream().filter(t -> !disable.test(t)).count() == selected.size());
allSelector.selectedProperty().addListener((observable, oldValue, newValue) -> {
cbs.forEach(checkBox -> {
if (checkBox.isDisabled()) {
return;
}
checkBox.setSelected(newValue);
});
});
var l = new Label(null, allSelector);
l.textProperty().bind(AppI18n.observable("selectAll"));
l.setGraphicTextGap(9);
l.setOnMouseClicked(event -> allSelector.setSelected(!allSelector.isSelected()));
vbox.getChildren().add(new Separator(Orientation.HORIZONTAL));
vbox.getChildren().add(l);
}
});
var l = new Label(null, allSelector);
l.textProperty().bind(AppI18n.observable("selectAll"));
l.setGraphicTextGap(9);
l.setOnMouseClicked(event -> allSelector.setSelected(!allSelector.isSelected()));
vbox.getChildren().add(new Separator(Orientation.HORIZONTAL));
vbox.getChildren().add(l);
}
});
var sp = new ScrollPane(vbox);
sp.setFitToWidth(true);
sp.getStyleClass().add("list-selector-comp");
@@ -86,8 +86,8 @@ public class LoadingOverlayComp extends Comp<CompStructure<StackPane>> {
r.heightProperty()));
loading.prefHeightProperty().bind(loading.prefWidthProperty());
stack.maxWidthProperty().bind(r.prefWidthProperty());
stack.maxHeightProperty().bind(r.prefHeightProperty());
stack.prefWidthProperty().bind(r.widthProperty());
stack.prefHeightProperty().bind(r.heightProperty());
return new SimpleCompStructure<>(stack);
}
@@ -117,6 +117,9 @@ public class ModalOverlayComp extends SimpleComp {
if (runnable != null) {
runnable.run();
}
if (oldValue.getContent() instanceof ModalOverlayContentComp mocc) {
mocc.setModalOverlay(null);
}
}
if (newValue != null) {
@@ -146,13 +149,6 @@ public class ModalOverlayComp extends SimpleComp {
closeButton.setVisible(false);
}
}
// Wait 2 pulses before focus so that the scene can be assigned to r
Platform.runLater(() -> {
Platform.runLater(() -> {
modalBox.requestFocus();
});
});
}
private Region toBox(ModalPane pane, ModalOverlay newValue) {
@@ -29,10 +29,16 @@ public class ModalOverlayStackComp extends SimpleComp {
private Comp<?> buildModalOverlay(Comp<?> current, int index) {
var prop = new SimpleObjectProperty<ModalOverlay>();
modalOverlay.subscribe(() -> {
var ex = prop.get();
// Don't shift just for an index change
if (ex != null && modalOverlay.contains(ex)) {
return;
}
prop.set(modalOverlay.size() > index ? modalOverlay.get(index) : null);
});
prop.addListener((observable, oldValue, newValue) -> {
if (newValue == null) {
if (newValue == null && modalOverlay.indexOf(oldValue) == index) {
modalOverlay.remove(oldValue);
}
});
@@ -234,7 +234,7 @@ public class StoreCreationComp extends DialogComp {
&& AppPrefs.get()
.openConnectionSearchWindowOnConnectionCreation()
.get()) {
ScanAlert.showAsync(e);
ScanDialog.showAsync(e);
}
if (selectCategory) {
@@ -4,7 +4,7 @@ import io.xpipe.app.comp.base.PrettyImageHelper;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.DataStoreCreationCategory;
import io.xpipe.app.ext.DataStoreProviders;
import io.xpipe.app.util.ScanAlert;
import io.xpipe.app.util.ScanDialog;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuButton;
@@ -22,7 +22,7 @@ public class StoreCreationMenu {
automatically.setGraphic(new FontIcon("mdi2e-eye-plus-outline"));
automatically.textProperty().bind(AppI18n.observable("addAutomatically"));
automatically.setOnAction(event -> {
ScanAlert.showAsync(null);
ScanDialog.showAsync(null);
event.consume();
});
menu.getItems().add(automatically);
@@ -6,7 +6,7 @@ import io.xpipe.app.core.AppFont;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.ScanAlert;
import io.xpipe.app.util.ScanDialog;
import io.xpipe.core.process.OsType;
import javafx.geometry.Insets;
@@ -38,7 +38,7 @@ public class StoreIntroComp extends SimpleComp {
var scanButton = new Button(null, new FontIcon("mdi2m-magnify"));
scanButton.textProperty().bind(AppI18n.observable("detectConnections"));
scanButton.setOnAction(event -> ScanAlert.showAsync(DataStorage.get().local()));
scanButton.setOnAction(event -> ScanDialog.showAsync(DataStorage.get().local()));
scanButton.setDefaultButton(true);
var scanPane = new StackPane(scanButton);
scanPane.setAlignment(Pos.CENTER);
@@ -33,7 +33,9 @@ public class AppDialog {
}
public static void closeDialog(ModalOverlay overlay) {
modalOverlay.remove(overlay);
PlatformThread.runLaterIfNeeded(() -> {
modalOverlay.remove(overlay);
});
}
public static void waitForClose() {
@@ -59,7 +59,7 @@ public class UserReportComp extends SimpleComp {
private Comp<?> createAttachments() {
var list = new ListSelectorComp<>(
event.getAttachments(),
FXCollections.observableList(event.getAttachments()),
file -> {
if (file.equals(AppLogs.get().getSessionLogsDirectory())) {
return AppI18n.get("logFilesAttachment");
@@ -69,7 +69,7 @@ public class UserReportComp extends SimpleComp {
},
includedDiagnostics,
file -> false,
false)
() -> false)
.styleClass("attachment-list");
return new TitledPaneComp(AppI18n.observable("additionalErrorAttachments"), list, 100)
.apply(struc -> struc.get().setExpanded(true))
@@ -11,27 +11,6 @@ import java.util.Optional;
public class CommandSupport {
public static String getPath(ShellControl sc) throws Exception {
var path = sc.command(sc.getShellDialect().getPrintEnvironmentVariableCommand("PATH"))
.readStdoutOrThrow();
return path;
}
public static String getLibraryPath(ShellControl sc) throws Exception {
var path = sc.command(sc.getShellDialect().getPrintEnvironmentVariableCommand("LD_LIBRARY_PATH"))
.readStdoutOrThrow();
return path;
}
public static boolean isRoot(ShellControl shellControl) throws Exception {
if (shellControl.getOsType() == OsType.WINDOWS) {
return false;
}
var isRoot = shellControl.executeSimpleBooleanCommand("test \"${EUID:-$(id -u)}\" -eq 0");
return isRoot;
}
public static Optional<String> findProgram(ShellControl processControl, String name) throws Exception {
var out = processControl
.command(processControl.getShellDialect().getWhichCommand(name))
@@ -1,74 +0,0 @@
package io.xpipe.app.util;
import io.xpipe.app.comp.base.ModalButton;
import io.xpipe.app.comp.base.ModalOverlay;
import io.xpipe.app.ext.ScanProvider;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.core.process.ShellControl;
import io.xpipe.core.process.ShellTtyState;
import io.xpipe.core.process.SystemState;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
public class ScanAlert {
public static void showAsync(DataStoreEntry entry) {
ThreadHelper.runAsync(() -> {
var showForCon = entry == null
|| (entry.getStore() instanceof ShellStore
&& (!(entry.getStorePersistentState() instanceof SystemState systemState)
|| systemState.getTtyState() == null
|| systemState.getTtyState() == ShellTtyState.NONE));
if (showForCon) {
showForShellStore(entry);
}
});
}
public static void showForShellStore(DataStoreEntry initial) {
show(initial, (DataStoreEntry entry, ShellControl sc) -> {
if (!sc.canHaveSubshells()) {
return null;
}
if (!sc.getShellDialect().getDumbMode().supportsAnyPossibleInteraction()) {
return null;
}
if (sc.getTtyState() != ShellTtyState.NONE) {
return null;
}
var providers = ScanProvider.getAll();
var applicable = new ArrayList<ScanProvider.ScanOpportunity>();
for (ScanProvider scanProvider : providers) {
try {
// Previous scan operation could have exited the shell
sc.start();
ScanProvider.ScanOpportunity operation = scanProvider.create(entry, sc);
if (operation != null) {
applicable.add(operation);
}
} catch (Exception ex) {
ErrorEvent.fromThrowable(ex).handle();
}
}
return applicable;
});
}
private static void show(
DataStoreEntry initialStore,
BiFunction<DataStoreEntry, ShellControl, List<ScanProvider.ScanOpportunity>> applicable) {
var comp = new ScanDialog(initialStore != null ? initialStore.ref() : null, applicable);
var modal = ModalOverlay.of("scanAlertTitle", comp);
modal.addButton(ModalButton.ok(() -> {
comp.finish();
}));
modal.showAndWait();
}
}
@@ -1,167 +1,82 @@
package io.xpipe.app.util;
import io.xpipe.app.comp.Comp;
import io.xpipe.app.comp.base.ListSelectorComp;
import io.xpipe.app.comp.base.ModalOverlayContentComp;
import io.xpipe.app.comp.store.StoreChoiceComp;
import io.xpipe.app.comp.store.StoreViewState;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.comp.base.ModalButton;
import io.xpipe.app.comp.base.ModalOverlay;
import io.xpipe.app.ext.ScanProvider;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.core.process.ShellControl;
import javafx.application.Platform;
import javafx.beans.property.*;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import io.xpipe.core.process.ShellTtyState;
import io.xpipe.core.process.SystemState;
import javafx.collections.ObservableList;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import static javafx.scene.layout.Priority.ALWAYS;
public class ScanDialog {
class ScanDialog extends ModalOverlayContentComp {
private final DataStoreEntryRef<ShellStore> initialStore;
private final BiFunction<DataStoreEntry, ShellControl, List<ScanProvider.ScanOpportunity>> applicable;
private final ObjectProperty<DataStoreEntryRef<ShellStore>> entry;
private final ListProperty<ScanProvider.ScanOpportunity> selected =
new SimpleListProperty<>(FXCollections.observableArrayList());
private final BooleanProperty busy = new SimpleBooleanProperty();
ScanDialog(
DataStoreEntryRef<ShellStore> entry,
BiFunction<DataStoreEntry, ShellControl, List<ScanProvider.ScanOpportunity>> applicable) {
this.initialStore = entry;
this.entry = new SimpleObjectProperty<>(entry);
this.applicable = applicable;
}
@Override
protected ObservableValue<Boolean> busy() {
return busy;
}
protected void finish() {
ThreadHelper.runFailableAsync(() -> {
if (entry.get() == null) {
return;
public static void showAsync(DataStoreEntry entry) {
ThreadHelper.runAsync(() -> {
var showForCon = entry == null
|| (entry.getStore() instanceof ShellStore
&& (!(entry.getStorePersistentState() instanceof SystemState systemState)
|| systemState.getTtyState() == null
|| systemState.getTtyState() == ShellTtyState.NONE));
if (showForCon) {
showForShellStore(entry);
}
});
}
Platform.runLater(() -> {
var modal = getModalOverlay();
modal.close();
});
public static void showForShellStore(DataStoreEntry initial) {
var action = new ScanDialogAction() {
BooleanScope.executeExclusive(busy, () -> {
entry.get().get().setExpanded(true);
var copy = new ArrayList<>(selected);
for (var a : copy) {
// If the user decided to remove the selected entry
// while the scan is running, just return instantly
if (!DataStorage.get()
.getStoreEntriesSet()
.contains(entry.get().get())) {
return;
}
@Override
public boolean scan(ObservableList<ScanProvider.ScanOpportunity> all, ObservableList<ScanProvider.ScanOpportunity> selected, DataStoreEntry entry, ShellControl sc) {
if (!sc.canHaveSubshells()) {
return false;
}
// Previous scan operation could have exited the shell
var sc = entry.get().getStore().getOrStartSession();
if (!sc.getShellDialect().getDumbMode().supportsAnyPossibleInteraction()) {
return false;
}
if (sc.getTtyState() != ShellTtyState.NONE) {
return false;
}
var providers = ScanProvider.getAll();
for (ScanProvider scanProvider : providers) {
try {
a.getProvider().scan(entry.get().get(), sc);
} catch (Throwable ex) {
// Previous scan operation could have exited the shell
sc.start();
ScanProvider.ScanOpportunity operation = scanProvider.create(entry, sc);
if (operation != null) {
if (!operation.isDisabled() && operation.isDefaultSelected()) {
selected.add(operation);
}
all.add(operation);
}
} catch (Exception ex) {
ErrorEvent.fromThrowable(ex).handle();
}
}
});
});
return true;
}
};
show(initial, action);
}
private void onUpdate(DataStoreEntryRef<ShellStore> newValue, StackPane stackPane) {
selected.clear();
stackPane.getChildren().clear();
if (newValue == null) {
return;
}
ThreadHelper.runFailableAsync(() -> {
BooleanScope.executeExclusive(busy, () -> {
var sc = entry.get().getStore().getOrStartSession();
var a = applicable.apply(entry.get().get(), sc);
Platform.runLater(() -> {
if (a == null) {
var modal = getModalOverlay();
if (modal != null) {
modal.close();
}
return;
}
selected.setAll(a.stream()
.filter(scanOperation -> scanOperation.isDefaultSelected() && !scanOperation.isDisabled())
.toList());
Function<ScanProvider.ScanOpportunity, String> nameFunc = (ScanProvider.ScanOpportunity s) -> {
var n = AppI18n.get(s.getNameKey());
if (s.getLicensedFeatureId() == null) {
return n;
}
var suffix = LicenseProvider.get().getFeature(s.getLicensedFeatureId());
return n
+ suffix.getDescriptionSuffix()
.map(d -> " (" + d + ")")
.orElse("");
};
var r = new ListSelectorComp<>(
a, nameFunc, selected, scanOperation -> scanOperation.isDisabled(), a.size() > 3)
.createRegion();
stackPane.getChildren().add(r);
});
});
});
}
@Override
protected Region createSimple() {
StackPane stackPane = new StackPane();
stackPane.getStyleClass().add("scan-list");
var b = new OptionsBuilder()
.name("scanAlertChoiceHeader")
.description("scanAlertChoiceHeaderDescription")
.addComp(new StoreChoiceComp<>(
StoreChoiceComp.Mode.OTHER,
null,
entry,
ShellStore.class,
store1 -> true,
StoreViewState.get().getAllConnectionsCategory())
.disable(new SimpleBooleanProperty(initialStore != null)))
.name("scanAlertHeader")
.description("scanAlertHeaderDescription")
.addComp(Comp.of(() -> stackPane).vgrow())
.buildComp()
.prefWidth(500)
.prefHeight(680)
.apply(struc -> {
VBox.setVgrow(struc.get().getChildren().get(1), ALWAYS);
});
entry.subscribe(newValue -> {
onUpdate(newValue, stackPane);
});
return b.createRegion();
private static void show(
DataStoreEntry initialStore,
ScanDialogAction action) {
var comp = new ScanDialogComp(initialStore != null ? initialStore.ref() : null, action);
var modal = ModalOverlay.of("scanAlertTitle", comp);
modal.addButton(ModalButton.ok(() -> {
comp.finish();
}));
modal.showAndWait();
}
}
@@ -0,0 +1,11 @@
package io.xpipe.app.util;
import io.xpipe.app.ext.ScanProvider;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.core.process.ShellControl;
import javafx.collections.ObservableList;
public interface ScanDialogAction {
boolean scan(ObservableList<ScanProvider.ScanOpportunity> all, ObservableList<ScanProvider.ScanOpportunity> selected, DataStoreEntry entry, ShellControl shellControl);
}
@@ -0,0 +1,172 @@
package io.xpipe.app.util;
import io.xpipe.app.comp.Comp;
import io.xpipe.app.comp.base.ListSelectorComp;
import io.xpipe.app.comp.base.ModalOverlayContentComp;
import io.xpipe.app.comp.store.StoreChoiceComp;
import io.xpipe.app.comp.store.StoreViewState;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.ScanProvider;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.core.process.ShellControl;
import javafx.application.Platform;
import javafx.beans.property.*;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import static javafx.scene.layout.Priority.ALWAYS;
class ScanDialogComp extends ModalOverlayContentComp {
private final DataStoreEntryRef<ShellStore> initialStore;
private final ScanDialogAction action;
private final ObjectProperty<DataStoreEntryRef<ShellStore>> entry;
private final ListProperty<ScanProvider.ScanOpportunity> available =
new SimpleListProperty<>(FXCollections.observableArrayList());
private final ListProperty<ScanProvider.ScanOpportunity> selected =
new SimpleListProperty<>(FXCollections.observableArrayList());
private final BooleanProperty busy = new SimpleBooleanProperty();
ScanDialogComp(
DataStoreEntryRef<ShellStore> entry,
ScanDialogAction action) {
this.initialStore = entry;
this.entry = new SimpleObjectProperty<>(entry);
this.action = action;
}
@Override
protected ObservableValue<Boolean> busy() {
return busy;
}
protected void finish() {
ThreadHelper.runFailableAsync(() -> {
if (entry.get() == null) {
return;
}
Platform.runLater(() -> {
var modal = getModalOverlay();
modal.close();
});
BooleanScope.executeExclusive(busy, () -> {
entry.get().get().setExpanded(true);
var copy = new ArrayList<>(selected);
for (var a : copy) {
// If the user decided to remove the selected entry
// while the scan is running, just return instantly
if (!DataStorage.get()
.getStoreEntriesSet()
.contains(entry.get().get())) {
return;
}
// Previous scan operation could have exited the shell
var sc = entry.get().getStore().getOrStartSession();
try {
a.getProvider().scan(entry.get().get(), sc);
} catch (Throwable ex) {
ErrorEvent.fromThrowable(ex).handle();
}
}
});
});
}
private void onUpdate(DataStoreEntryRef<ShellStore> newValue) {
available.clear();
selected.clear();
if (newValue == null) {
return;
}
ThreadHelper.runFailableAsync(() -> {
BooleanScope.executeExclusive(busy, () -> {
boolean r;
try {
var sc = entry.get().getStore().getOrStartSession();
r = action.scan(available, selected, newValue.get(), sc);
} catch (Throwable t) {
var modal = getModalOverlay();
if (initialStore != null && modal != null) {
modal.close();
}
throw t;
}
if (!r) {
var modal = getModalOverlay();
if (initialStore != null && modal != null) {
modal.close();
}
}
});
});
}
@Override
protected Region createSimple() {
StackPane stackPane = new StackPane();
stackPane.getStyleClass().add("scan-list");
var b = new OptionsBuilder()
.name("scanAlertChoiceHeader")
.description("scanAlertChoiceHeaderDescription")
.addComp(new StoreChoiceComp<>(
StoreChoiceComp.Mode.OTHER,
null,
entry,
ShellStore.class,
store1 -> true,
StoreViewState.get().getAllConnectionsCategory())
.disable(new SimpleBooleanProperty(initialStore != null)))
.name("scanAlertHeader")
.description("scanAlertHeaderDescription")
.addComp(Comp.of(() -> stackPane).vgrow())
.buildComp()
.prefWidth(500)
.prefHeight(680)
.apply(struc -> {
VBox.setVgrow(struc.get().getChildren().get(1), ALWAYS);
});
Function<ScanProvider.ScanOpportunity, String> nameFunc = (ScanProvider.ScanOpportunity s) -> {
var n = AppI18n.get(s.getNameKey());
if (s.getLicensedFeatureId() == null) {
return n;
}
var suffix = LicenseProvider.get().getFeature(s.getLicensedFeatureId());
return n
+ suffix.getDescriptionSuffix()
.map(d -> " (" + d + ")")
.orElse("");
};
var r = new ListSelectorComp<>(
available, nameFunc, selected, scanOperation -> scanOperation.isDisabled(), () -> available.size() > 3)
.createRegion();
stackPane.getChildren().add(r);
entry.subscribe(newValue -> {
onUpdate(newValue);
});
return b.createRegion();
}
}
@@ -3,6 +3,7 @@ package io.xpipe.core.process;
import io.xpipe.core.store.FilePath;
import java.io.InputStream;
import java.util.Optional;
public class ShellView {
@@ -45,4 +46,38 @@ public class ShellView {
public String user() throws Exception {
return getDialect().printUsernameCommand(shellControl).readStdoutOrThrow();
}
public String getPath() throws Exception {
var path = shellControl.command(shellControl.getShellDialect().getPrintEnvironmentVariableCommand("PATH"))
.readStdoutOrThrow();
return path;
}
public String getLibraryPath() throws Exception {
var path = shellControl.command(shellControl.getShellDialect().getPrintEnvironmentVariableCommand("LD_LIBRARY_PATH"))
.readStdoutOrThrow();
return path;
}
public boolean isRoot() throws Exception {
if (shellControl.getOsType() == OsType.WINDOWS) {
return false;
}
var isRoot = shellControl.executeSimpleBooleanCommand("test \"${EUID:-$(id -u)}\" -eq 0");
return isRoot;
}
public Optional<String> findProgram(String name) throws Exception {
var out = shellControl
.command(shellControl.getShellDialect().getWhichCommand(name))
.readStdoutIfPossible();
return out.flatMap(s -> s.lines().findFirst()).map(String::trim);
}
public boolean isInPath(String executable) throws Exception {
return shellControl.executeSimpleBooleanCommand(
shellControl.getShellDialect().getWhichCommand(executable));
}
}
@@ -5,7 +5,7 @@ import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.ext.ShellStore;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.util.ScanAlert;
import io.xpipe.app.util.ScanDialog;
import io.xpipe.core.process.ShellTtyState;
import io.xpipe.core.process.SystemState;
@@ -69,7 +69,7 @@ public class ScanStoreAction implements ActionProvider {
@Override
public void execute() {
if (entry == null || entry.getStore() instanceof ShellStore) {
ScanAlert.showForShellStore(entry);
ScanDialog.showForShellStore(entry);
}
}
}
@@ -301,10 +301,10 @@ public interface SshIdentityStrategy {
var file = getFile(sc);
var dir = FileNames.getParent(file);
if (sc.getOsType() == OsType.WINDOWS) {
var path = CommandSupport.getPath(sc);
var path = sc.view().getPath();
builder.fixedEnvrironment("PATH", dir + ";" + path);
} else {
var path = CommandSupport.getLibraryPath(sc);
var path = sc.view().getLibraryPath();
builder.fixedEnvrironment("LD_LIBRARY_PATH", dir + ":" + path);
}
})
@@ -348,10 +348,10 @@ public interface SshIdentityStrategy {
var file = getFile();
var dir = FileNames.getParent(file);
if (sc.getOsType() == OsType.WINDOWS) {
var path = CommandSupport.getPath(sc);
var path = sc.view().getPath();
builder.fixedEnvrironment("PATH", dir + ";" + path);
} else {
var path = CommandSupport.getLibraryPath(sc);
var path = sc.view().getLibraryPath();
builder.fixedEnvrironment("LD_LIBRARY_PATH", dir + ":" + path);
}
});
@@ -146,7 +146,7 @@ public class SimpleScriptStoreProvider implements EnabledParentStoreProvider, Da
}
};
var selectedExecTypes = new SimpleListProperty<>(FXCollections.observableList(selectedStart));
var selectorComp = new ListSelectorComp<>(vals, name, selectedExecTypes, v -> false, false);
var selectorComp = new ListSelectorComp<>(FXCollections.observableList(vals), name, selectedExecTypes, v -> false, () -> false);
return new OptionsBuilder()
.name("snippets")
Binary file not shown.