This commit is contained in:
crschnick 2024-07-25 07:01:40 +00:00
parent 54f27a274f
commit 17069267da
95 changed files with 618 additions and 383 deletions

View file

@ -122,7 +122,8 @@ public class AppBeaconServer {
var file = XPipeInstallation.getLocalBeaconAuthFile();
try {
Files.delete(file);
} catch (IOException ignored) {}
} catch (IOException ignored) {
}
}
private void start() throws IOException {

View file

@ -108,7 +108,7 @@ public class BeaconRequestHandler<T> implements HttpHandler {
// Make deserialization error message more readable
var message = ex.getMessage()
.replace("$RequestBuilder", "")
.replace("Exchange$Request","Request")
.replace("Exchange$Request", "Request")
.replace("at [Source: UNKNOWN; byte offset: #UNKNOWN]", "")
.replaceAll("(\\w+) is marked non-null but is null", "field $1 is missing from object")
.trim();
@ -125,8 +125,12 @@ public class BeaconRequestHandler<T> implements HttpHandler {
var emptyResponseClass = beaconInterface.getResponseClass().getDeclaredFields().length == 0;
if (!emptyResponseClass && response != null) {
TrackEvent.trace("Sending response:\n" + response);
TrackEvent.trace("Sending raw response:\n" + JacksonMapper.getCensored().valueToTree(response).toPrettyString());
var bytes = JacksonMapper.getDefault().valueToTree(response).toPrettyString().getBytes(StandardCharsets.UTF_8);
TrackEvent.trace("Sending raw response:\n"
+ JacksonMapper.getCensored().valueToTree(response).toPrettyString());
var bytes = JacksonMapper.getDefault()
.valueToTree(response)
.toPrettyString()
.getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, bytes.length);
try (OutputStream os = exchange.getResponseBody()) {
os.write(bytes);

View file

@ -1,12 +1,13 @@
package io.xpipe.app.beacon.impl;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.beacon.api.ConnectionAddExchange;
import io.xpipe.core.util.ValidationException;
import com.sun.net.httpserver.HttpExchange;
public class ConnectionAddExchangeImpl extends ConnectionAddExchange {
@Override

View file

@ -1,6 +1,5 @@
package io.xpipe.app.beacon.impl;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.browser.session.BrowserSessionModel;
import io.xpipe.app.core.AppLayoutModel;
import io.xpipe.app.storage.DataStorage;
@ -8,6 +7,8 @@ import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.ConnectionBrowseExchange;
import io.xpipe.core.store.FileSystemStore;
import com.sun.net.httpserver.HttpExchange;
public class ConnectionBrowseExchangeImpl extends ConnectionBrowseExchange {
@Override
@ -18,7 +19,8 @@ public class ConnectionBrowseExchangeImpl extends ConnectionBrowseExchange {
if (!(e.getStore() instanceof FileSystemStore)) {
throw new BeaconClientException("Not a file system connection");
}
BrowserSessionModel.DEFAULT.openFileSystemSync(e.ref(),msg.getDirectory() != null ? ignored -> msg.getDirectory() : null,null);
BrowserSessionModel.DEFAULT.openFileSystemSync(
e.ref(), msg.getDirectory() != null ? ignored -> msg.getDirectory() : null, null);
AppLayoutModel.get().selectBrowser();
return Response.builder().build();
}

View file

@ -28,9 +28,16 @@ public class ConnectionInfoExchangeImpl extends ConnectionInfoExchange {
.orElseThrow())
.getNames();
var cat = new StorePath(names.subList(1, names.size()));
var cache = e.getStoreCache().entrySet().stream().filter(stringObjectEntry -> {
return stringObjectEntry.getValue() != null && (ClassUtils.isPrimitiveOrWrapper(stringObjectEntry.getValue().getClass()) || stringObjectEntry.getValue() instanceof String);
}).collect(Collectors.toMap(stringObjectEntry -> stringObjectEntry.getKey(),stringObjectEntry -> stringObjectEntry.getValue()));
var cache = e.getStoreCache().entrySet().stream()
.filter(stringObjectEntry -> {
return stringObjectEntry.getValue() != null
&& (ClassUtils.isPrimitiveOrWrapper(
stringObjectEntry.getValue().getClass())
|| stringObjectEntry.getValue() instanceof String);
})
.collect(Collectors.toMap(
stringObjectEntry -> stringObjectEntry.getKey(),
stringObjectEntry -> stringObjectEntry.getValue()));
var apply = InfoResponse.builder()
.lastModified(e.getLastModified())
@ -50,27 +57,17 @@ public class ConnectionInfoExchangeImpl extends ConnectionInfoExchange {
}
private Class<?> toWrapper(Class<?> clazz) {
if (!clazz.isPrimitive())
return clazz;
if (!clazz.isPrimitive()) return clazz;
if (clazz == Integer.TYPE)
return Integer.class;
if (clazz == Long.TYPE)
return Long.class;
if (clazz == Boolean.TYPE)
return Boolean.class;
if (clazz == Byte.TYPE)
return Byte.class;
if (clazz == Character.TYPE)
return Character.class;
if (clazz == Float.TYPE)
return Float.class;
if (clazz == Double.TYPE)
return Double.class;
if (clazz == Short.TYPE)
return Short.class;
if (clazz == Void.TYPE)
return Void.class;
if (clazz == Integer.TYPE) return Integer.class;
if (clazz == Long.TYPE) return Long.class;
if (clazz == Boolean.TYPE) return Boolean.class;
if (clazz == Byte.TYPE) return Byte.class;
if (clazz == Character.TYPE) return Character.class;
if (clazz == Float.TYPE) return Float.class;
if (clazz == Double.TYPE) return Double.class;
if (clazz == Short.TYPE) return Short.class;
if (clazz == Void.TYPE) return Void.class;
return clazz;
}

View file

@ -1,11 +1,12 @@
package io.xpipe.app.beacon.impl;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.FixedHierarchyStore;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.ConnectionRefreshExchange;
import com.sun.net.httpserver.HttpExchange;
public class ConnectionRefreshExchangeImpl extends ConnectionRefreshExchange {
@Override

View file

@ -1,11 +1,12 @@
package io.xpipe.app.beacon.impl;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.ConnectionRemoveExchange;
import com.sun.net.httpserver.HttpExchange;
import java.util.ArrayList;
import java.util.UUID;

View file

@ -1,12 +1,13 @@
package io.xpipe.app.beacon.impl;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.TerminalLauncher;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.ConnectionTerminalExchange;
import io.xpipe.core.store.ShellStore;
import com.sun.net.httpserver.HttpExchange;
public class ConnectionTerminalExchangeImpl extends ConnectionTerminalExchange {
@Override
@ -18,7 +19,7 @@ public class ConnectionTerminalExchangeImpl extends ConnectionTerminalExchange {
throw new BeaconClientException("Not a shell connection");
}
try (var sc = shellStore.control().start()) {
TerminalLauncher.open(e,e.getName(),msg.getDirectory(),sc);
TerminalLauncher.open(e, e.getName(), msg.getDirectory(), sc);
}
return Response.builder().build();
}

View file

@ -1,11 +1,12 @@
package io.xpipe.app.beacon.impl;
import com.sun.net.httpserver.HttpExchange;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.beacon.BeaconClientException;
import io.xpipe.beacon.api.ConnectionToggleExchange;
import io.xpipe.core.store.SingletonSessionStore;
import com.sun.net.httpserver.HttpExchange;
public class ConnectionToggleExchangeImpl extends ConnectionToggleExchange {
@Override

View file

@ -35,9 +35,13 @@ public final class BrowserBookmarkHeaderComp extends SimpleComp {
.apply(struc -> {
AppFont.medium(struc.get());
});
var filter = new FilterComp(this.filter).styleClass(Styles.RIGHT_PILL).minWidth(0).hgrow().apply(struc -> {
AppFont.medium(struc.get());
});
var filter = new FilterComp(this.filter)
.styleClass(Styles.RIGHT_PILL)
.minWidth(0)
.hgrow()
.apply(struc -> {
AppFont.medium(struc.get());
});
var top = new HorizontalComp(List.of(category, filter))
.apply(struc -> struc.get().setFillHeight(true))

View file

@ -12,10 +12,12 @@ import io.xpipe.app.fxcomps.impl.HorizontalComp;
import io.xpipe.app.fxcomps.impl.LabelComp;
import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.util.HumanReadableFormat;
import javafx.beans.binding.Bindings;
import javafx.geometry.Pos;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.Region;
import lombok.EqualsAndHashCode;
import lombok.Value;
@ -37,8 +39,7 @@ public class BrowserStatusBarComp extends SimpleComp {
createProgressEstimateStatus(),
Comp.hspacer(),
createClipboardStatus(),
createSelectionStatus()
));
createSelectionStatus()));
bar.spacing(15);
bar.styleClass("status-bar");
@ -58,12 +59,16 @@ public class BrowserStatusBarComp extends SimpleComp {
return null;
} else {
var expected = p.expectedTimeRemaining();
var show = p.elapsedTime().compareTo(Duration.of(200, ChronoUnit.MILLIS)) > 0 && (p.getTotal() > 50_000_000 || expected.toMillis() > 5000);
var show = p.elapsedTime().compareTo(Duration.of(200, ChronoUnit.MILLIS)) > 0
&& (p.getTotal() > 50_000_000 || expected.toMillis() > 5000);
var time = show ? HumanReadableFormat.duration(p.expectedTimeRemaining()) : "";
return time;
}
});
var progressComp = new LabelComp(text).styleClass("progress").apply(struc -> struc.get().setAlignment(Pos.CENTER_LEFT)).prefWidth(90);
var progressComp = new LabelComp(text)
.styleClass("progress")
.apply(struc -> struc.get().setAlignment(Pos.CENTER_LEFT))
.prefWidth(90);
return progressComp;
}
@ -77,7 +82,10 @@ public class BrowserStatusBarComp extends SimpleComp {
return transferred + " / " + all;
}
});
var progressComp = new LabelComp(text).styleClass("progress").apply(struc -> struc.get().setAlignment(Pos.CENTER_LEFT)).prefWidth(150);
var progressComp = new LabelComp(text)
.styleClass("progress")
.apply(struc -> struc.get().setAlignment(Pos.CENTER_LEFT))
.prefWidth(150);
return progressComp;
}
@ -89,7 +97,10 @@ public class BrowserStatusBarComp extends SimpleComp {
return p.getName();
}
});
var progressComp = new LabelComp(text).styleClass("progress").apply(struc -> struc.get().setAlignment(Pos.CENTER_LEFT)).prefWidth(180);
var progressComp = new LabelComp(text)
.styleClass("progress")
.apply(struc -> struc.get().setAlignment(Pos.CENTER_LEFT))
.prefWidth(180);
return progressComp;
}
@ -160,7 +171,6 @@ public class BrowserStatusBarComp extends SimpleComp {
emptyEntry.onDragDone(event);
});
// Use status bar as an extension of file list
new ContextMenuAugment<>(
mouseEvent -> mouseEvent.getButton() == MouseButton.SECONDARY,

View file

@ -9,6 +9,7 @@ import io.xpipe.app.fxcomps.augment.DragOverPseudoClassAugment;
import io.xpipe.app.fxcomps.impl.*;
import io.xpipe.app.fxcomps.util.DerivedObservableList;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
@ -19,6 +20,7 @@ import javafx.scene.input.ClipboardContent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.Region;
import org.kordamp.ikonli.javafx.FontIcon;
import java.io.File;
@ -47,25 +49,29 @@ public class BrowserTransferComp extends SimpleComp {
var binding = new DerivedObservableList<>(model.getItems(), true)
.mapped(item -> item.getBrowserEntry())
.getList();
var list = new BrowserSelectionListComp(
binding,
entry -> {
var sourceItem = model.getCurrentItems().stream()
.filter(item -> item.getBrowserEntry() == entry)
.findAny();
if (sourceItem.isEmpty()) {
return new SimpleStringProperty("?");
}
synchronized (sourceItem.get().getProgress()) {
return Bindings.createStringBinding(() -> {
var list = new BrowserSelectionListComp(binding, entry -> {
var sourceItem = model.getCurrentItems().stream()
.filter(item -> item.getBrowserEntry() == entry)
.findAny();
if (sourceItem.isEmpty()) {
return new SimpleStringProperty("?");
}
synchronized (sourceItem.get().getProgress()) {
return Bindings.createStringBinding(
() -> {
var p = sourceItem.get().getProgress().getValue();
var progressSuffix = p == null || sourceItem.get().downloadFinished().get() ?
"" :
" " + (p.getTransferred() * 100 / p.getTotal()) + "%";
var progressSuffix = p == null
|| sourceItem
.get()
.downloadFinished()
.get()
? ""
: " " + (p.getTransferred() * 100 / p.getTotal()) + "%";
return entry.getFileName() + progressSuffix;
}, sourceItem.get().getProgress());
}
})
},
sourceItem.get().getProgress());
}
})
.grow(false, true);
var dragNotice = new LabelComp(AppI18n.observable("dragLocalFiles"))
.apply(struc -> struc.get().setGraphic(new FontIcon("mdi2h-hand-left")))
@ -74,111 +80,110 @@ public class BrowserTransferComp extends SimpleComp {
.hide(model.getEmpty());
var clearButton = new IconButtonComp("mdi2c-close", () -> {
ThreadHelper.runAsync(() -> {
model.clear(true);
});
ThreadHelper.runAsync(() -> {
model.clear(true);
});
})
.hide(model.getEmpty())
.tooltipKey("clearTransferDescription");
var downloadButton = new IconButtonComp("mdi2f-folder-move-outline", () -> {
ThreadHelper.runFailableAsync(() -> {
model.transferToDownloads();
});
})
ThreadHelper.runFailableAsync(() -> {
model.transferToDownloads();
});
})
.hide(model.getEmpty())
.tooltipKey("downloadStageDescription");
var bottom =
new HorizontalComp(List.of(Comp.hspacer(), dragNotice, Comp.hspacer(), downloadButton, Comp.hspacer(4), clearButton));
var bottom = new HorizontalComp(
List.of(Comp.hspacer(), dragNotice, Comp.hspacer(), downloadButton, Comp.hspacer(4), clearButton));
var listBox = new VerticalComp(List.of(list, bottom))
.spacing(5)
.padding(new Insets(10, 10, 5, 10))
.apply(struc -> struc.get().setMinHeight(200))
.apply(struc -> struc.get().setMaxHeight(200));
var stack = new StackComp(List.of(backgroundStack, listBox))
.apply(DragOverPseudoClassAugment.create())
.apply(struc -> {
struc.get().setOnDragOver(event -> {
// Accept drops from inside the app window
if (event.getGestureSource() != null && event.getGestureSource() != struc.get()) {
event.acceptTransferModes(TransferMode.ANY);
event.consume();
}
});
struc.get().setOnDragDropped(event -> {
// Accept drops from inside the app window
if (event.getGestureSource() != null) {
var drag = BrowserClipboard.retrieveDrag(event.getDragboard());
if (drag == null) {
return;
.apply(DragOverPseudoClassAugment.create())
.apply(struc -> {
struc.get().setOnDragOver(event -> {
// Accept drops from inside the app window
if (event.getGestureSource() != null && event.getGestureSource() != struc.get()) {
event.acceptTransferModes(TransferMode.ANY);
event.consume();
}
});
struc.get().setOnDragDropped(event -> {
// Accept drops from inside the app window
if (event.getGestureSource() != null) {
var drag = BrowserClipboard.retrieveDrag(event.getDragboard());
if (drag == null) {
return;
}
if (!(model.getBrowserSessionModel()
.getSelectedEntry()
.getValue()
instanceof OpenFileSystemModel fileSystemModel)) {
return;
}
var files = drag.getEntries();
model.drop(fileSystemModel, files);
event.setDropCompleted(true);
event.consume();
}
});
struc.get().setOnDragDetected(event -> {
var items = model.getCurrentItems();
var selected = items.stream()
.map(item -> item.getBrowserEntry())
.toList();
var files = items.stream()
.filter(item -> item.downloadFinished().get())
.map(item -> {
try {
var file = item.getLocalFile();
if (!Files.exists(file)) {
return Optional.<File>empty();
}
return Optional.of(file.toRealPath().toFile());
} catch (IOException e) {
throw new RuntimeException(e);
}
})
.flatMap(Optional::stream)
.toList();
if (files.isEmpty()) {
return;
}
if (!(model.getBrowserSessionModel()
.getSelectedEntry()
.getValue()
instanceof OpenFileSystemModel fileSystemModel)) {
return;
}
var cc = new ClipboardContent();
cc.putFiles(files);
Dragboard db = struc.get().startDragAndDrop(TransferMode.COPY);
db.setContent(cc);
var files = drag.getEntries();
model.drop(fileSystemModel, files);
event.setDropCompleted(true);
event.consume();
}
});
struc.get().setOnDragDetected(event -> {
var items = model.getCurrentItems();
var selected = items.stream()
.map(item -> item.getBrowserEntry())
.toList();
var files = items.stream()
.filter(item -> item.downloadFinished().get())
.map(item -> {
try {
var file = item.getLocalFile();
if (!Files.exists(file)) {
return Optional.<File>empty();
}
Image image = BrowserSelectionListComp.snapshot(FXCollections.observableList(selected));
db.setDragView(image, -20, 15);
return Optional.of(
file.toRealPath().toFile());
} catch (IOException e) {
throw new RuntimeException(e);
}
})
.flatMap(Optional::stream)
.toList();
if (files.isEmpty()) {
return;
}
event.setDragDetect(true);
event.consume();
});
struc.get().setOnDragDone(event -> {
if (!event.isAccepted()) {
return;
}
var cc = new ClipboardContent();
cc.putFiles(files);
Dragboard db = struc.get().startDragAndDrop(TransferMode.COPY);
db.setContent(cc);
Image image = BrowserSelectionListComp.snapshot(FXCollections.observableList(selected));
db.setDragView(image, -20, 15);
event.setDragDetect(true);
event.consume();
});
struc.get().setOnDragDone(event -> {
if (!event.isAccepted()) {
return;
}
// The files might not have been transferred yet
// We can't listen to this, so just don't delete them
model.clear(false);
event.consume();
});
});
// The files might not have been transferred yet
// We can't listen to this, so just don't delete them
model.clear(false);
event.consume();
});
});
stack.apply(struc -> {
model.getBrowserSessionModel().getDraggingFiles().addListener((observable, oldValue, newValue) -> {
struc.get().pseudoClassStateChanged(PseudoClass.getPseudoClass("highlighted"),newValue);
struc.get().pseudoClassStateChanged(PseudoClass.getPseudoClass("highlighted"), newValue);
});
});

View file

@ -10,12 +10,14 @@ import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.util.DesktopHelper;
import io.xpipe.app.util.ShellTemp;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableBooleanValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import lombok.Value;
import org.apache.commons.io.FileUtils;
@ -38,17 +40,19 @@ public class BrowserTransferModel {
public BrowserTransferModel(BrowserSessionModel browserSessionModel) {
this.browserSessionModel = browserSessionModel;
var thread = ThreadHelper.createPlatformThread("file downloader", true,() -> {
while (true) {
Optional<Item> toDownload;
synchronized (items) {
toDownload = items.stream().filter(item -> !item.downloadFinished().get()).findFirst();
}
if (toDownload.isPresent()) {
downloadSingle(toDownload.get());
}
ThreadHelper.sleep(20);
}
var thread = ThreadHelper.createPlatformThread("file downloader", true, () -> {
while (true) {
Optional<Item> toDownload;
synchronized (items) {
toDownload = items.stream()
.filter(item -> !item.downloadFinished().get())
.findFirst();
}
if (toDownload.isPresent()) {
downloadSingle(toDownload.get());
}
ThreadHelper.sleep(20);
}
});
thread.start();
}
@ -78,7 +82,8 @@ public class BrowserTransferModel {
public void clear(boolean delete) {
List<Item> toClear;
synchronized (items) {
toClear = items.stream().filter(item -> item.downloadFinished().get()).toList();
toClear =
items.stream().filter(item -> item.downloadFinished().get()).toList();
if (toClear.isEmpty()) {
return;
}
@ -114,40 +119,41 @@ public class BrowserTransferModel {
return;
}
if (item.downloadFinished().get()) {
return;
}
if (item.downloadFinished().get()) {
return;
}
if (item.getOpenFileSystemModel() != null
&& item.getOpenFileSystemModel().isClosed()) {
return;
}
if (item.getOpenFileSystemModel() != null
&& item.getOpenFileSystemModel().isClosed()) {
return;
}
try {
var op = new BrowserFileTransferOperation(
LocalFileSystem.getLocalFileEntry(TEMP),
List.of(item.getBrowserEntry().getRawFileEntry()),
BrowserFileTransferMode.COPY,
false,
progress -> {
synchronized (item.getProgress()) {
item.getProgress().setValue(progress);
}
item.getOpenFileSystemModel().getProgress().setValue(progress);
});
op.execute();
} catch (Throwable t) {
ErrorEvent.fromThrowable(t).handle();
synchronized (items) {
items.remove(item);
}
try {
var op = new BrowserFileTransferOperation(
LocalFileSystem.getLocalFileEntry(TEMP),
List.of(item.getBrowserEntry().getRawFileEntry()),
BrowserFileTransferMode.COPY,
false,
progress -> {
synchronized (item.getProgress()) {
item.getProgress().setValue(progress);
}
item.getOpenFileSystemModel().getProgress().setValue(progress);
});
op.execute();
} catch (Throwable t) {
ErrorEvent.fromThrowable(t).handle();
synchronized (items) {
items.remove(item);
}
}
}
public void transferToDownloads() throws Exception {
List<Item> toMove;
synchronized (items) {
toMove = items.stream().filter(item -> item.downloadFinished().get()).toList();
toMove =
items.stream().filter(item -> item.downloadFinished().get()).toList();
if (toMove.isEmpty()) {
return;
}

View file

@ -1,7 +1,5 @@
package io.xpipe.app.browser;
import atlantafx.base.controls.Spacer;
import atlantafx.base.theme.Styles;
import io.xpipe.app.browser.session.BrowserSessionModel;
import io.xpipe.app.comp.base.ButtonComp;
import io.xpipe.app.comp.base.ListBoxViewComp;
@ -18,6 +16,7 @@ import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.fxcomps.util.DerivedObservableList;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
@ -31,6 +30,9 @@ import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import atlantafx.base.controls.Spacer;
import atlantafx.base.theme.Styles;
import java.util.List;
public class BrowserWelcomeComp extends SimpleComp {

View file

@ -80,7 +80,10 @@ public class BrowserAlerts {
private static String getSelectedElementsString(List<FileSystem.FileEntry> source) {
var namesHeader = AppI18n.get("selectedElements");
var names = namesHeader + "\n"
+ source.stream().limit(10).map(entry -> "- " + new FilePath(entry.getPath()).getFileName()).collect(Collectors.joining("\n"));
+ source.stream()
.limit(10)
.map(entry -> "- " + new FilePath(entry.getPath()).getFileName())
.collect(Collectors.joining("\n"));
if (source.size() > 10) {
names += "\n+ " + (source.size() - 10) + " ...";
}

View file

@ -1,7 +1,5 @@
package io.xpipe.app.browser.file;
import atlantafx.base.controls.Spacer;
import atlantafx.base.theme.Styles;
import io.xpipe.app.browser.action.BrowserAction;
import io.xpipe.app.comp.base.LazyTextFieldComp;
import io.xpipe.app.core.AppI18n;
@ -13,6 +11,7 @@ import io.xpipe.core.process.OsType;
import io.xpipe.core.store.FileKind;
import io.xpipe.core.store.FileNames;
import io.xpipe.core.store.FileSystem;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
@ -33,6 +32,9 @@ import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import atlantafx.base.controls.Spacer;
import atlantafx.base.theme.Styles;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
@ -133,15 +135,18 @@ public final class BrowserFileListComp extends SimpleComp {
private void prepareTypedSelectionModel(TableView<BrowserEntry> table) {
AtomicReference<Instant> lastFail = new AtomicReference<>();
table.addEventHandler(KeyEvent.KEY_PRESSED,event -> {
table.addEventHandler(KeyEvent.KEY_PRESSED, event -> {
var typed = event.getText();
if (typed.isEmpty()) {
return;
}
var updated = typedSelection.get() + typed;
var find = fileList.getShown().getValue().stream().filter(browserEntry -> browserEntry.getFileName().toLowerCase().startsWith(updated.toLowerCase())).findFirst();
if (find.isEmpty()) {
var found = fileList.getShown().getValue().stream()
.filter(browserEntry ->
browserEntry.getFileName().toLowerCase().startsWith(updated.toLowerCase()))
.findFirst();
if (found.isEmpty()) {
if (lastFail.get() == null) {
lastFail.set(Instant.now());
}
@ -161,8 +166,9 @@ public final class BrowserFileListComp extends SimpleComp {
lastFail.set(null);
typedSelection.set(updated);
table.scrollTo(find.get());
table.getSelectionModel().clearAndSelect(fileList.getShown().getValue().indexOf(find.get()));
table.scrollTo(found.get());
table.getSelectionModel()
.clearAndSelect(fileList.getShown().getValue().indexOf(found.get()));
event.consume();
});
@ -176,7 +182,7 @@ public final class BrowserFileListComp extends SimpleComp {
lastFail.set(null);
});
table.addEventFilter(KeyEvent.KEY_PRESSED,event -> {
table.addEventFilter(KeyEvent.KEY_PRESSED, event -> {
if (event.getCode() == KeyCode.ESCAPE) {
typedSelection.set("");
lastFail.set(null);
@ -269,7 +275,6 @@ public final class BrowserFileListComp extends SimpleComp {
emptyEntry.onDragDone(event);
});
// Don't let the list view see this event
// otherwise it unselects everything as it doesn't understand shift clicks
table.addEventFilter(MouseEvent.MOUSE_CLICKED, t -> {
@ -350,7 +355,6 @@ public final class BrowserFileListComp extends SimpleComp {
listEntry.get().onDragDone(event);
});
return row;
});
}

View file

@ -93,7 +93,7 @@ public class BrowserFileListCompEntry {
return false;
}
public void onMouseShiftClick(MouseEvent t) {
if (t.getButton() != MouseButton.PRIMARY) {
return;

View file

@ -104,19 +104,23 @@ public final class BrowserFileListModel {
// This check will fail on case-insensitive file systems when changing the case of the file
// So skip it in this case
var skipExistCheck = fileSystemModel.getFileSystem().getShell().orElseThrow().getOsType() == OsType.WINDOWS && old.getFileName()
.equalsIgnoreCase(newName);
var skipExistCheck =
fileSystemModel.getFileSystem().getShell().orElseThrow().getOsType() == OsType.WINDOWS
&& old.getFileName().equalsIgnoreCase(newName);
if (!skipExistCheck) {
boolean exists;
try {
exists = fileSystemModel.getFileSystem().fileExists(newFullPath) || fileSystemModel.getFileSystem().directoryExists(newFullPath);
exists = fileSystemModel.getFileSystem().fileExists(newFullPath)
|| fileSystemModel.getFileSystem().directoryExists(newFullPath);
} catch (Exception e) {
ErrorEvent.fromThrowable(e).handle();
return old;
}
if (exists) {
ErrorEvent.fromMessage("Target " + newFullPath + " does already exist").expected().handle();
ErrorEvent.fromMessage("Target " + newFullPath + " does already exist")
.expected()
.handle();
fileSystemModel.refresh();
return old;
}

View file

@ -224,7 +224,13 @@ public class BrowserFileTransferOperation {
updateProgress(BrowserTransferProgress.finished(source.getName(), totalSize.get()));
}
private void transfer(FileSystem.FileEntry sourceFile, String targetFile, AtomicLong transferred, AtomicLong totalSize, Instant start) throws Exception {
private void transfer(
FileSystem.FileEntry sourceFile,
String targetFile,
AtomicLong transferred,
AtomicLong totalSize,
Instant start)
throws Exception {
InputStream inputStream = null;
OutputStream outputStream = null;
try {

View file

@ -24,8 +24,10 @@ import io.xpipe.core.process.ShellDialects;
import io.xpipe.core.process.ShellOpenFunction;
import io.xpipe.core.store.*;
import io.xpipe.core.util.FailableConsumer;
import javafx.beans.binding.Bindings;
import javafx.beans.property.*;
import lombok.Getter;
import lombok.SneakyThrows;
@ -113,7 +115,8 @@ public final class OpenFileSystemModel extends BrowserSessionTab<FileSystemStore
&& savedState != null
&& current != null) {
savedState.cd(current.getPath(), false);
BrowserSavedStateImpl.get().add(new BrowserSavedState.Entry(getEntry().get().getUuid(), current.getPath()));
BrowserSavedStateImpl.get()
.add(new BrowserSavedState.Entry(getEntry().get().getUuid(), current.getPath()));
}
try {
fileSystem.close();

View file

@ -82,22 +82,24 @@ public class OpenFileSystemSavedState {
if (delay) {
// After 10 seconds
TIMEOUT_TIMER.schedule(new TimerTask() {
@Override
public void run() {
// Synchronize with platform thread
Platform.runLater(() -> {
if (model.isClosed()) {
return;
}
TIMEOUT_TIMER.schedule(
new TimerTask() {
@Override
public void run() {
// Synchronize with platform thread
Platform.runLater(() -> {
if (model.isClosed()) {
return;
}
if (Objects.equals(lastDirectory, dir)) {
updateRecent(dir);
save();
if (Objects.equals(lastDirectory, dir)) {
updateRecent(dir);
save();
}
});
}
});
}
}, 10000);
},
10000);
} else {
updateRecent(dir);
save();

View file

@ -2,12 +2,14 @@ package io.xpipe.app.browser.session;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import lombok.Getter;
@Getter

View file

@ -65,7 +65,6 @@ public class BrowserFileChooserModel extends BrowserAbstractSessionModel<OpenFil
onFinish.accept(stores);
}
public void finishWithoutChoice() {
synchronized (BrowserFileChooserModel.this) {
var open = selectedEntry.getValue();

View file

@ -16,6 +16,7 @@ import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.store.ShellStore;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
@ -103,16 +104,15 @@ public class BrowserSessionComp extends SimpleComp {
new VerticalComp(List.of(bookmarkTopBar, bookmarksContainer, localDownloadStage)).styleClass("left");
var split = new SimpleDoubleProperty();
var tabs = new BrowserSessionTabsComp(model, split)
.apply(struc -> {
struc.get().setViewOrder(1);
struc.get().setPickOnBounds(false);
AnchorPane.setTopAnchor(struc.get(), 0.0);
AnchorPane.setBottomAnchor(struc.get(), 0.0);
AnchorPane.setLeftAnchor(struc.get(), 0.0);
AnchorPane.setRightAnchor(struc.get(), 0.0);
});
var loadingIndicator = LoadingOverlayComp.noProgress(Comp.empty(),model.getBusy())
var tabs = new BrowserSessionTabsComp(model, split).apply(struc -> {
struc.get().setViewOrder(1);
struc.get().setPickOnBounds(false);
AnchorPane.setTopAnchor(struc.get(), 0.0);
AnchorPane.setBottomAnchor(struc.get(), 0.0);
AnchorPane.setLeftAnchor(struc.get(), 0.0);
AnchorPane.setRightAnchor(struc.get(), 0.0);
});
var loadingIndicator = LoadingOverlayComp.noProgress(Comp.empty(), model.getBusy())
.apply(struc -> {
AnchorPane.setTopAnchor(struc.get(), 0.0);
AnchorPane.setRightAnchor(struc.get(), 0.0);

View file

@ -80,7 +80,8 @@ public class BrowserSessionModel extends BrowserAbstractSessionModel<BrowserSess
public void openFileSystemSync(
DataStoreEntryRef<? extends FileSystemStore> store,
FailableFunction<OpenFileSystemModel, String, Exception> path,
BooleanProperty externalBusy) throws Exception {
BooleanProperty externalBusy)
throws Exception {
if (store == null) {
return;
}

View file

@ -1,7 +1,5 @@
package io.xpipe.app.browser.session;
import atlantafx.base.controls.RingProgressIndicator;
import atlantafx.base.theme.Styles;
import io.xpipe.app.browser.BrowserWelcomeComp;
import io.xpipe.app.comp.base.MultiContentComp;
import io.xpipe.app.core.AppI18n;
@ -15,6 +13,7 @@ import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.ContextMenuHelper;
import io.xpipe.app.util.InputHelper;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleBooleanProperty;
@ -32,6 +31,9 @@ import javafx.scene.input.KeyCombination;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import atlantafx.base.controls.RingProgressIndicator;
import atlantafx.base.theme.Styles;
import java.util.*;
import static atlantafx.base.theme.Styles.DENSE;
@ -50,11 +52,10 @@ public class BrowserSessionTabsComp extends SimpleComp {
public Region createSimple() {
var map = new LinkedHashMap<Comp<?>, ObservableValue<Boolean>>();
map.put(Comp.hspacer().styleClass("top-spacer"),
new SimpleBooleanProperty(true));
map.put(Comp.of(() -> createTabPane()),
Bindings.isNotEmpty(model.getSessionEntries()));
map.put(new BrowserWelcomeComp(model).apply(struc -> StackPane.setAlignment(struc.get(), Pos.CENTER_LEFT)),
map.put(Comp.hspacer().styleClass("top-spacer"), new SimpleBooleanProperty(true));
map.put(Comp.of(() -> createTabPane()), Bindings.isNotEmpty(model.getSessionEntries()));
map.put(
new BrowserWelcomeComp(model).apply(struc -> StackPane.setAlignment(struc.get(), Pos.CENTER_LEFT)),
Bindings.createBooleanBinding(
() -> {
return model.getSessionEntries().size() == 0;
@ -227,7 +228,8 @@ public class BrowserSessionTabsComp extends SimpleComp {
var forward = new KeyCodeCombination(KeyCode.TAB, KeyCombination.CONTROL_DOWN);
if (forward.match(keyEvent)) {
var next = (tabs.getSelectionModel().getSelectedIndex() + 1) % tabs.getTabs().size();
var next = (tabs.getSelectionModel().getSelectedIndex() + 1)
% tabs.getTabs().size();
tabs.getSelectionModel().select(next);
keyEvent.consume();
return;
@ -235,7 +237,8 @@ public class BrowserSessionTabsComp extends SimpleComp {
var back = new KeyCodeCombination(KeyCode.TAB, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN);
if (back.match(keyEvent)) {
var previous = (tabs.getTabs().size() + tabs.getSelectionModel().getSelectedIndex() - 1) % tabs.getTabs().size();
var previous = (tabs.getTabs().size() + tabs.getSelectionModel().getSelectedIndex() - 1)
% tabs.getTabs().size();
tabs.getSelectionModel().select(previous);
keyEvent.consume();
return;
@ -249,12 +252,18 @@ public class BrowserSessionTabsComp extends SimpleComp {
var cm = ContextMenuHelper.create();
var select = ContextMenuHelper.item(LabelGraphic.none(), AppI18n.get("selectTab"));
select.acceleratorProperty().bind(Bindings.createObjectBinding(() -> {
var start = KeyCode.F1.getCode();
var index = tabs.getTabs().indexOf(tab);
var keyCode = Arrays.stream(KeyCode.values()).filter(code -> code.getCode() == start + index).findAny().orElse(null);
return keyCode != null ? new KeyCodeCombination(keyCode) : null;
}, tabs.getTabs()));
select.acceleratorProperty()
.bind(Bindings.createObjectBinding(
() -> {
var start = KeyCode.F1.getCode();
var index = tabs.getTabs().indexOf(tab);
var keyCode = Arrays.stream(KeyCode.values())
.filter(code -> code.getCode() == start + index)
.findAny()
.orElse(null);
return keyCode != null ? new KeyCodeCombination(keyCode) : null;
},
tabs.getTabs()));
select.setOnAction(event -> {
tabs.getSelectionModel().select(tab);
event.consume();
@ -263,7 +272,7 @@ public class BrowserSessionTabsComp extends SimpleComp {
cm.getItems().add(new SeparatorMenuItem());
var close = ContextMenuHelper.item(LabelGraphic.none(), AppI18n.get("closeTab" ));
var close = ContextMenuHelper.item(LabelGraphic.none(), AppI18n.get("closeTab"));
close.setAccelerator(new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN));
close.setOnAction(event -> {
tabs.getTabs().remove(tab);
@ -273,7 +282,8 @@ public class BrowserSessionTabsComp extends SimpleComp {
var closeOthers = ContextMenuHelper.item(LabelGraphic.none(), AppI18n.get("closeOtherTabs"));
closeOthers.setOnAction(event -> {
tabs.getTabs().removeAll(tabs.getTabs().stream().filter(t -> t != tab).toList());
tabs.getTabs()
.removeAll(tabs.getTabs().stream().filter(t -> t != tab).toList());
event.consume();
});
cm.getItems().add(closeOthers);
@ -281,7 +291,10 @@ public class BrowserSessionTabsComp extends SimpleComp {
var closeLeft = ContextMenuHelper.item(LabelGraphic.none(), AppI18n.get("closeLeftTabs"));
closeLeft.setOnAction(event -> {
var index = tabs.getTabs().indexOf(tab);
tabs.getTabs().removeAll(tabs.getTabs().stream().filter(t -> tabs.getTabs().indexOf(t) < index).toList());
tabs.getTabs()
.removeAll(tabs.getTabs().stream()
.filter(t -> tabs.getTabs().indexOf(t) < index)
.toList());
event.consume();
});
cm.getItems().add(closeLeft);
@ -289,13 +302,17 @@ public class BrowserSessionTabsComp extends SimpleComp {
var closeRight = ContextMenuHelper.item(LabelGraphic.none(), AppI18n.get("closeRightTabs"));
closeRight.setOnAction(event -> {
var index = tabs.getTabs().indexOf(tab);
tabs.getTabs().removeAll(tabs.getTabs().stream().filter(t -> tabs.getTabs().indexOf(t) > index).toList());
tabs.getTabs()
.removeAll(tabs.getTabs().stream()
.filter(t -> tabs.getTabs().indexOf(t) > index)
.toList());
event.consume();
});
cm.getItems().add(closeRight);
var closeAll = ContextMenuHelper.item(LabelGraphic.none(), AppI18n.get("closeAllTabs"));
closeAll.setAccelerator(new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN));
closeAll.setAccelerator(
new KeyCodeCombination(KeyCode.W, KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN));
closeAll.setOnAction(event -> {
tabs.getTabs().clear();
event.consume();

View file

@ -10,6 +10,7 @@ import io.xpipe.app.fxcomps.CompStructure;
import io.xpipe.app.fxcomps.SimpleCompStructure;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.storage.DataStorage;
import javafx.beans.binding.Bindings;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.ButtonBase;

View file

@ -11,6 +11,7 @@ import io.xpipe.app.fxcomps.impl.TooltipAugment;
import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.update.UpdateAvailableAlert;
import io.xpipe.app.update.XPipeDistributionType;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
@ -41,14 +42,14 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
var selectedBorder = Bindings.createObjectBinding(
() -> {
var c = Platform.getPreferences().getAccentColor().desaturate();
return new Background(new BackgroundFill(c,new CornerRadii(8), new Insets(10, 1, 10, 2)));
return new Background(new BackgroundFill(c, new CornerRadii(8), new Insets(10, 1, 10, 2)));
},
Platform.getPreferences().accentColorProperty());
var hoverBorder = Bindings.createObjectBinding(
() -> {
var c = Platform.getPreferences().getAccentColor().darker().desaturate();
return new Background(new BackgroundFill(c,new CornerRadii(8), new Insets(10, 1, 10, 2)));
return new Background(new BackgroundFill(c, new CornerRadii(8), new Insets(10, 1, 10, 2)));
},
Platform.getPreferences().accentColorProperty());
@ -86,7 +87,8 @@ public class SideMenuBarComp extends Comp<CompStructure<VBox>> {
b.accessibleText(e.name());
var indicator = Comp.empty().styleClass("indicator");
var stack = new StackComp(List.of(indicator, b)).apply(struc -> struc.get().setAlignment(Pos.CENTER_RIGHT));
var stack = new StackComp(List.of(indicator, b))
.apply(struc -> struc.get().setAlignment(Pos.CENTER_RIGHT));
stack.apply(struc -> {
var indicatorRegion = (Region) struc.get().getChildren().getFirst();
indicatorRegion.setMaxWidth(7);

View file

@ -3,6 +3,7 @@ package io.xpipe.app.comp.store;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.augment.GrowAugment;
import io.xpipe.app.fxcomps.util.PlatformThread;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.geometry.HPos;
@ -30,7 +31,9 @@ public class DenseStoreEntryComp extends StoreEntryComp {
: Comp.empty();
information.setGraphic(state.createRegion());
var info = getWrapper().getEntry().getProvider() != null ? getWrapper().getEntry().getProvider().informationString(section) : new SimpleStringProperty();
var info = getWrapper().getEntry().getProvider() != null
? getWrapper().getEntry().getProvider().informationString(section)
: new SimpleStringProperty();
var summary = getWrapper().getSummary();
if (getWrapper().getEntry().getProvider() != null) {
information

View file

@ -3,6 +3,7 @@ package io.xpipe.app.comp.store;
import io.xpipe.app.core.AppFont;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.core.process.OsType;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;

View file

@ -58,7 +58,7 @@ public abstract class StoreEntryComp extends SimpleComp {
this.section = section;
this.content = content;
}
public StoreEntryWrapper getWrapper() {
return section.getWrapper();
}
@ -80,9 +80,7 @@ public abstract class StoreEntryComp extends SimpleComp {
} else {
var forceCondensed = AppPrefs.get() != null
&& AppPrefs.get().condenseConnectionDisplay().get();
return forceCondensed
? new DenseStoreEntryComp(e, true, null)
: new StandardStoreEntryComp(e, null);
return forceCondensed ? new DenseStoreEntryComp(e, true, null) : new StandardStoreEntryComp(e, null);
}
}
@ -137,7 +135,9 @@ public abstract class StoreEntryComp extends SimpleComp {
var loading = LoadingOverlayComp.noProgress(
Comp.of(() -> button),
getWrapper().getEntry().getValidity().isUsable()
? getWrapper().getBusy().or(getWrapper().getEntry().getProvider().busy(getWrapper()))
? getWrapper()
.getBusy()
.or(getWrapper().getEntry().getProvider().busy(getWrapper()))
: getWrapper().getBusy());
AppFont.normal(button);
return loading.createRegion();
@ -194,7 +194,8 @@ public abstract class StoreEntryComp extends SimpleComp {
protected Node createIcon(int w, int h) {
var img = getWrapper().disabledProperty().get()
? "disabled_icon.png"
: getWrapper().getEntry()
: getWrapper()
.getEntry()
.getProvider()
.getDisplayIconFileName(getWrapper().getEntry().getStore());
var imageComp = PrettyImageHelper.ofFixedSize(img, w, h);
@ -252,8 +253,11 @@ public abstract class StoreEntryComp extends SimpleComp {
leaf != null
? () -> {
ThreadHelper.runFailableAsync(() -> {
getWrapper().runAction(
leaf.createAction(getWrapper().getEntry().ref()), leaf.showBusy());
getWrapper()
.runAction(
leaf.createAction(
getWrapper().getEntry().ref()),
leaf.showBusy());
});
}
: null);
@ -323,8 +327,8 @@ public abstract class StoreEntryComp extends SimpleComp {
if (AppPrefs.get().developerMode().getValue()) {
var browse = new MenuItem(AppI18n.get("browseInternalStorage"), new FontIcon("mdi2f-folder-open-outline"));
browse.setOnAction(
event -> DesktopHelper.browsePathLocal(getWrapper().getEntry().getDirectory()));
browse.setOnAction(event ->
DesktopHelper.browsePathLocal(getWrapper().getEntry().getDirectory()));
contextMenu.getItems().add(browse);
var copyId = new MenuItem(AppI18n.get("copyId"), new FontIcon("mdi2c-content-copy"));
@ -466,19 +470,23 @@ public abstract class StoreEntryComp extends SimpleComp {
run.textProperty().bind(AppI18n.observable("base.execute"));
run.setOnAction(event -> {
ThreadHelper.runFailableAsync(() -> {
getWrapper().runAction(leaf.createAction(getWrapper().getEntry().ref()), leaf.showBusy());
getWrapper()
.runAction(leaf.createAction(getWrapper().getEntry().ref()), leaf.showBusy());
});
});
menu.getItems().add(run);
var sc = new MenuItem(null, new FontIcon("mdi2c-code-greater-than"));
var url = "xpipe://action/" + p.getId() + "/" + getWrapper().getEntry().getUuid();
var url = "xpipe://action/" + p.getId() + "/"
+ getWrapper().getEntry().getUuid();
sc.textProperty().bind(AppI18n.observable("base.createShortcut"));
sc.setOnAction(event -> {
ThreadHelper.runFailableAsync(() -> {
DesktopShortcuts.create(
url,
DataStorage.get().getStoreEntryDisplayName(getWrapper().getEntry()) + " ("
DataStorage.get()
.getStoreEntryDisplayName(
getWrapper().getEntry()) + " ("
+ p.getLeafDataStoreCallSite()
.getName(getWrapper().getEntry().ref())
.getValue() + ")");

View file

@ -151,7 +151,8 @@ public class StoreEntryWrapper {
summary.setValue(null);
} else {
try {
summary.setValue(entry.getProvider() != null ? entry.getProvider().summaryString(this) : null);
summary.setValue(
entry.getProvider() != null ? entry.getProvider().summaryString(this) : null);
} catch (Exception ex) {
// Summary creation might fail or have a bug
ErrorEvent.fromThrowable(ex).handle();
@ -167,8 +168,8 @@ public class StoreEntryWrapper {
.filter(e -> entry.getStore() != null
&& e.getDefaultDataStoreCallSite() != null
&& e.getDefaultDataStoreCallSite()
.getApplicableClass()
.isAssignableFrom(entry.getStore().getClass())
.getApplicableClass()
.isAssignableFrom(entry.getStore().getClass())
&& e.getDefaultDataStoreCallSite().isApplicable(entry.ref()))
.findFirst()
.orElse(null);

View file

@ -1,6 +1,5 @@
package io.xpipe.app.comp.store;
import atlantafx.base.controls.Popover;
import io.xpipe.app.comp.base.ButtonComp;
import io.xpipe.app.comp.base.DialogComp;
import io.xpipe.app.comp.base.MarkdownEditorComp;
@ -11,6 +10,7 @@ import io.xpipe.app.fxcomps.CompStructure;
import io.xpipe.app.fxcomps.impl.IconButtonComp;
import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.storage.DataStorage;
import javafx.application.Platform;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
@ -18,6 +18,8 @@ import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.paint.Color;
import atlantafx.base.controls.Popover;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

View file

@ -45,8 +45,7 @@ public class StoreQuickAccessButtonComp extends Comp<CompStructure<Button>> {
w.getEntry().getProvider().getDisplayIconFileName(w.getEntry().getStore());
if (c.getList().isEmpty()) {
var item = ContextMenuHelper.item(
new LabelGraphic.ImageGraphic(graphic, 16),
w.getName().getValue());
new LabelGraphic.ImageGraphic(graphic, 16), w.getName().getValue());
item.setOnAction(event -> {
action.accept(section);
contextMenu.hide();

View file

@ -65,8 +65,7 @@ public interface StoreSortMode {
.isUsable())
.map(this::representative),
Stream.of(s))
.max(Comparator.comparing(
section -> date(section)))
.max(Comparator.comparing(section -> date(section)))
.orElseThrow();
}
@ -103,8 +102,7 @@ public interface StoreSortMode {
.isUsable())
.map(this::representative),
Stream.of(s))
.max(Comparator.comparing(
section -> date(section)))
.max(Comparator.comparing(section -> date(section)))
.orElseThrow();
}

View file

@ -7,9 +7,11 @@ import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.update.XPipeDistributionType;
import io.xpipe.app.util.LicenseProvider;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.stage.Stage;
import lombok.Getter;
import lombok.SneakyThrows;

View file

@ -105,8 +105,9 @@ public class AppExtensionManager {
var sourceVersion = AppVersion.parse(sv)
.orElseThrow(() -> new IllegalArgumentException("Invalid source version: " + sv));
if (AppProperties.get().isLocatorVersionCheck() && !installVersion.equals(sourceVersion)) {
throw new IllegalStateException("Incompatible development version. Source: " + iv + ", Installation: "
+ sv + "\n\nPlease try to check out the matching release version in the repository. See https://github.com/xpipe-io/xpipe/blob/master/CONTRIBUTING.md#development-setup");
throw new IllegalStateException(
"Incompatible development version. Source: " + iv + ", Installation: " + sv
+ "\n\nPlease try to check out the matching release version in the repository. See https://github.com/xpipe-io/xpipe/blob/master/CONTRIBUTING.md#development-setup");
}
var extensions = XPipeInstallation.getLocalExtensionsDirectory(p);

View file

@ -128,5 +128,6 @@ public class AppLayoutModel {
double browserConnectionsWidth;
}
public record Entry(ObservableValue<String> name, String icon, Comp<?> comp, Runnable action, KeyCombination combination) {}
public record Entry(
ObservableValue<String> name, String icon, Comp<?> comp, Runnable action, KeyCombination combination) {}
}

View file

@ -43,7 +43,10 @@ public class AppTheme {
public static void initThemeHandlers(Stage stage) {
Runnable r = () -> {
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass(OsType.getLocal().getId()), true);
stage.getScene()
.getRoot()
.pseudoClassStateChanged(
PseudoClass.getPseudoClass(OsType.getLocal().getId()), true);
if (AppPrefs.get() == null) {
var def = Theme.getDefaultLightTheme();
stage.getScene().getRoot().getStyleClass().add(def.getCssId());
@ -207,7 +210,6 @@ public class AppTheme {
Application.setUserAgentStylesheet(Styles.toDataURI(builder.toString()));
}
public List<String> getAdditionalStylesheets() {
return List.of();
}

View file

@ -4,11 +4,11 @@ import io.xpipe.app.core.mode.OperationMode;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.core.process.OsType;
import javax.imageio.ImageIO;
import java.awt.*;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import javax.imageio.ImageIO;
public class AppTrayIcon {

View file

@ -18,9 +18,9 @@ public class AppRosettaCheck {
var ret = LocalShell.getShell().executeSimpleStringCommand("sysctl -n sysctl.proc_translated");
if (ret.equals("1")) {
ErrorEvent.fromMessage("You are running the Intel version of XPipe on an Apple Silicon system." +
" There is a native build available that comes with much better performance." +
" Please install that one instead.");
ErrorEvent.fromMessage("You are running the Intel version of XPipe on an Apple Silicon system."
+ " There is a native build available that comes with much better performance."
+ " Please install that one instead.");
}
}
}

View file

@ -5,6 +5,7 @@ import io.xpipe.app.util.LocalShell;
import io.xpipe.core.process.OsType;
import io.xpipe.core.process.ProcessControlProvider;
import io.xpipe.core.process.ProcessOutputException;
import lombok.Value;
import java.util.Optional;

View file

@ -16,7 +16,9 @@ import io.xpipe.app.util.XPipeSession;
import io.xpipe.core.util.FailableRunnable;
import io.xpipe.core.util.XPipeDaemonMode;
import io.xpipe.core.util.XPipeInstallation;
import javafx.application.Platform;
import lombok.Getter;
import java.util.ArrayList;
@ -97,7 +99,9 @@ public abstract class OperationMode {
Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> {
// It seems like a few exceptions are thrown in the quantum renderer
// when in shutdown. We can ignore these
if (OperationMode.isInShutdown() && Platform.isFxApplicationThread() && ex instanceof NullPointerException) {
if (OperationMode.isInShutdown()
&& Platform.isFxApplicationThread()
&& ex instanceof NullPointerException) {
return;
}

View file

@ -8,6 +8,7 @@ import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.update.UpdateAvailableAlert;
import io.xpipe.app.util.PlatformState;
import io.xpipe.app.util.ThreadHelper;
import javafx.application.Application;
public abstract class PlatformMode extends OperationMode {

View file

@ -11,6 +11,7 @@ import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.prefs.CloseBehaviourAlert;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.process.OsType;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.geometry.Rectangle2D;
@ -21,17 +22,18 @@ import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.stage.Screen;
import javafx.stage.Stage;
import lombok.Builder;
import lombok.Getter;
import lombok.Value;
import lombok.extern.jackson.Jacksonized;
import javax.imageio.ImageIO;
import java.io.IOException;
import java.nio.file.Path;
import java.time.Duration;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import javax.imageio.ImageIO;
public class AppMainWindow {

View file

@ -8,6 +8,7 @@ import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.InputHelper;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.process.OsType;
import javafx.application.Platform;
import javafx.beans.value.ObservableValue;
import javafx.css.PseudoClass;
@ -25,7 +26,6 @@ import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;

View file

@ -3,6 +3,7 @@ package io.xpipe.app.core.window;
import io.xpipe.app.fxcomps.util.PlatformThread;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.core.process.OsType;
import javafx.animation.PauseTransition;
import javafx.application.Platform;
import javafx.collections.ListChangeListener;
@ -12,6 +13,7 @@ import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.Window;
import javafx.util.Duration;
import org.apache.commons.lang3.SystemUtils;
public class ModifiedStage extends Stage {
@ -55,8 +57,10 @@ public class ModifiedStage extends Stage {
return;
}
var applyToStage = (OsType.getLocal() == OsType.WINDOWS) ||
(OsType.getLocal() == OsType.MACOS && AppMainWindow.getInstance() != null && AppMainWindow.getInstance().getStage() == stage);
var applyToStage = (OsType.getLocal() == OsType.WINDOWS)
|| (OsType.getLocal() == OsType.MACOS
&& AppMainWindow.getInstance() != null
&& AppMainWindow.getInstance().getStage() == stage);
if (!applyToStage || AppPrefs.get() == null || AppPrefs.get().theme.getValue() == null) {
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("seamless-frame"), false);
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("separate-frame"), true);
@ -64,14 +68,19 @@ public class ModifiedStage extends Stage {
}
switch (OsType.getLocal()) {
case OsType.Linux linux -> {
}
case OsType.Linux linux -> {}
case OsType.MacOs macOs -> {
var ctrl = new NativeMacOsWindowControl(stage);
var seamlessFrame = !AppPrefs.get().performanceMode().get() && mergeFrame();
var seamlessFrameApplied = ctrl.setAppearance(seamlessFrame, AppPrefs.get().theme.getValue().isDark()) && seamlessFrame;
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("seamless-frame"), seamlessFrameApplied);
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("separate-frame"), !seamlessFrameApplied);
var seamlessFrameApplied = ctrl.setAppearance(
seamlessFrame, AppPrefs.get().theme.getValue().isDark())
&& seamlessFrame;
stage.getScene()
.getRoot()
.pseudoClassStateChanged(PseudoClass.getPseudoClass("seamless-frame"), seamlessFrameApplied);
stage.getScene()
.getRoot()
.pseudoClassStateChanged(PseudoClass.getPseudoClass("separate-frame"), !seamlessFrameApplied);
}
case OsType.Windows windows -> {
var ctrl = new NativeWinWindowControl(stage);
@ -84,8 +93,12 @@ public class ModifiedStage extends Stage {
} else {
seamlessFrame = ctrl.setWindowBackdrop(NativeWinWindowControl.DwmSystemBackDropType.MICA_ALT);
}
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("seamless-frame"), seamlessFrame);
stage.getScene().getRoot().pseudoClassStateChanged(PseudoClass.getPseudoClass("separate-frame"), !seamlessFrame);
stage.getScene()
.getRoot()
.pseudoClassStateChanged(PseudoClass.getPseudoClass("seamless-frame"), seamlessFrame);
stage.getScene()
.getRoot()
.pseudoClassStateChanged(PseudoClass.getPseudoClass("separate-frame"), !seamlessFrame);
}
}
}

View file

@ -1,11 +1,13 @@
package io.xpipe.app.core.window;
import com.sun.jna.NativeLong;
import io.xpipe.app.core.AppProperties;
import io.xpipe.app.util.NativeBridge;
import io.xpipe.app.util.ThreadHelper;
import io.xpipe.core.util.ModuleHelper;
import javafx.stage.Window;
import com.sun.jna.NativeLong;
import lombok.Getter;
import lombok.SneakyThrows;

View file

@ -145,9 +145,16 @@ public interface ActionProvider {
.map(actionProviderProvider -> actionProviderProvider.get())
.toList());
var menuProviders = ALL.stream().map(actionProvider -> actionProvider.getBranchDataStoreCallSite() != null ?
actionProvider.getBranchDataStoreCallSite().getChildren() : List.of()).flatMap(List::stream).toList();
ALL_STANDALONE.addAll(ALL.stream().filter(actionProvider -> menuProviders.stream().noneMatch(menuItem -> menuItem.getClass().equals(actionProvider.getClass()))).toList());
var menuProviders = ALL.stream()
.map(actionProvider -> actionProvider.getBranchDataStoreCallSite() != null
? actionProvider.getBranchDataStoreCallSite().getChildren()
: List.of())
.flatMap(List::stream)
.toList();
ALL_STANDALONE.addAll(ALL.stream()
.filter(actionProvider -> menuProviders.stream()
.noneMatch(menuItem -> menuItem.getClass().equals(actionProvider.getClass())))
.toList());
}
}
}

View file

@ -13,6 +13,7 @@ import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.core.store.DataStore;
import io.xpipe.core.util.JacksonizedValue;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;

View file

@ -68,7 +68,10 @@ public class DataStoreProviders {
throw new IllegalStateException("Not initialized");
}
return (T) ALL.stream().filter(d -> d.getStoreClasses().contains(store.getClass())).findAny().orElseThrow(() -> new IllegalArgumentException("Unknown store class"));
return (T) ALL.stream()
.filter(d -> d.getStoreClasses().contains(store.getClass()))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("Unknown store class"));
}
public static List<DataStoreProvider> getAll() {

View file

@ -3,6 +3,7 @@ package io.xpipe.app.fxcomps.impl;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.CompStructure;
import io.xpipe.app.fxcomps.SimpleCompStructure;
import javafx.scene.layout.AnchorPane;
import java.util.List;

View file

@ -1,7 +1,5 @@
package io.xpipe.app.fxcomps.impl;
import atlantafx.base.controls.Popover;
import atlantafx.base.theme.Styles;
import io.xpipe.app.comp.base.ButtonComp;
import io.xpipe.app.comp.store.*;
import io.xpipe.app.core.AppFont;
@ -15,6 +13,7 @@ import io.xpipe.app.util.DataStoreCategoryChoiceComp;
import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.LocalStore;
import io.xpipe.core.store.ShellStore;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
@ -26,6 +25,9 @@ import javafx.scene.control.MenuButton;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import atlantafx.base.controls.Popover;
import atlantafx.base.theme.Styles;
import lombok.RequiredArgsConstructor;
import org.kordamp.ikonli.javafx.FontIcon;

View file

@ -1,16 +1,18 @@
package io.xpipe.app.fxcomps.impl;
import atlantafx.base.controls.CustomTextField;
import io.xpipe.app.core.AppActionLinkDetector;
import io.xpipe.app.core.AppI18n;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.CompStructure;
import io.xpipe.app.fxcomps.SimpleCompStructure;
import io.xpipe.app.fxcomps.util.PlatformThread;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
import javafx.scene.Cursor;
import javafx.scene.input.MouseButton;
import atlantafx.base.controls.CustomTextField;
import org.kordamp.ikonli.javafx.FontIcon;
import java.util.Objects;
@ -39,9 +41,16 @@ public class FilterComp extends Comp<CompStructure<CustomTextField>> {
filter.setMaxHeight(2000);
filter.getStyleClass().add("filter-comp");
filter.promptTextProperty().bind(AppI18n.observable("searchFilter"));
filter.rightProperty().bind(Bindings.createObjectBinding(() -> {
return filter.isFocused() || (filter.getText() != null && !filter.getText().isEmpty()) ? clear : fi;
}, filter.focusedProperty()));
filter.rightProperty()
.bind(Bindings.createObjectBinding(
() -> {
return filter.isFocused()
|| (filter.getText() != null
&& !filter.getText().isEmpty())
? clear
: fi;
},
filter.focusedProperty()));
filter.setAccessibleText("Filter");
filterText.subscribe(val -> {

View file

@ -2,7 +2,9 @@ package io.xpipe.app.fxcomps.util;
import io.xpipe.app.fxcomps.Comp;
import io.xpipe.app.fxcomps.impl.PrettyImageHelper;
import javafx.scene.Node;
import lombok.EqualsAndHashCode;
import lombok.Value;
import org.kordamp.ikonli.javafx.FontIcon;
@ -33,7 +35,6 @@ public abstract class LabelGraphic {
}
}
@Value
@EqualsAndHashCode(callSuper = true)
public static class ImageGraphic extends LabelGraphic {

View file

@ -3,6 +3,7 @@ package io.xpipe.app.fxcomps.util;
import io.xpipe.app.core.mode.OperationMode;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.util.PlatformState;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
@ -11,6 +12,7 @@ import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import lombok.NonNull;
import java.util.*;

View file

@ -98,9 +98,11 @@ public class LauncherCommand implements Callable<Integer> {
}
} catch (Exception ex) {
var cli = XPipeInstallation.getLocalDefaultCliExecutable();
ErrorEvent.fromThrowable("Unable to connect to existing running daemon instance as it did not respond."
+ " Either try to kill the process xpiped manually or use the command \"" + cli
+ "\" daemon stop --force.", ex)
ErrorEvent.fromThrowable(
"Unable to connect to existing running daemon instance as it did not respond."
+ " Either try to kill the process xpiped manually or use the command \"" + cli
+ "\" daemon stop --force.",
ex)
.term()
.expected()
.handle();
@ -127,9 +129,8 @@ public class LauncherCommand implements Callable<Integer> {
// there might be another instance running, for example
// starting up or listening on another port
if (!AppDataLock.lock()) {
TrackEvent.info(
"Data directory " + AppProperties.get().getDataDir().toString()
+ " is already locked. Is another instance running?");
TrackEvent.info("Data directory " + AppProperties.get().getDataDir().toString()
+ " is already locked. Is another instance running?");
OperationMode.halt(1);
}

View file

@ -129,7 +129,8 @@ public interface ExternalEditorType extends PrefsChoiceValue {
List.of(VSCODIUM_WINDOWS, VSCODE_INSIDERS_WINDOWS, VSCODE_WINDOWS, NOTEPADPLUSPLUS, NOTEPAD);
List<LinuxPathType> LINUX_EDITORS =
List.of(VSCODIUM_LINUX, VSCODE_LINUX, ZED_LINUX, KATE, GEDIT, PLUMA, LEAFPAD, MOUSEPAD, GNOME);
List<ExternalEditorType> MACOS_EDITORS = List.of(BBEDIT, VSCODIUM_MACOS, VSCODE_MACOS, SUBLIME_MACOS, ZED_MACOS, TEXT_EDIT);
List<ExternalEditorType> MACOS_EDITORS =
List.of(BBEDIT, VSCODIUM_MACOS, VSCODE_MACOS, SUBLIME_MACOS, ZED_MACOS, TEXT_EDIT);
List<ExternalEditorType> CROSS_PLATFORM_EDITORS = List.of(FLEET, INTELLIJ, PYCHARM, WEBSTORM, CLION);
@SuppressWarnings("TrivialFunctionalExpressionUsage")

View file

@ -22,9 +22,13 @@ public class SyncCategory extends AppPrefsCategory {
.sub(new OptionsBuilder()
.name("enableGitStorage")
.description(
AppProperties.get().isStaging() && !prefs.developerMode().getValue() ? "enableGitStoragePtbDisabled" : "enableGitStorageDescription")
AppProperties.get().isStaging()
&& !prefs.developerMode().getValue()
? "enableGitStoragePtbDisabled"
: "enableGitStorageDescription")
.addToggle(prefs.enableGitStorage)
.disable(AppProperties.get().isStaging() && !prefs.developerMode().getValue())
.disable(AppProperties.get().isStaging()
&& !prefs.developerMode().getValue())
.nameAndDescription("storageGitRemote")
.addString(prefs.storageGitRemote, true)
.disable(prefs.enableGitStorage.not())

View file

@ -10,7 +10,9 @@ import io.xpipe.core.store.FixedChildStore;
import io.xpipe.core.store.LocalStore;
import io.xpipe.core.store.StorePath;
import io.xpipe.core.util.UuidHelper;
import javafx.util.Pair;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
@ -339,7 +341,7 @@ public abstract class DataStorage {
@SneakyThrows
public boolean refreshChildren(DataStoreEntry e) {
return refreshChildren(e,false);
return refreshChildren(e, false);
}
public boolean refreshChildren(DataStoreEntry e, boolean throwOnFail) throws Exception {
@ -350,7 +352,9 @@ public abstract class DataStorage {
e.incrementBusyCounter();
List<? extends DataStoreEntryRef<? extends FixedChildStore>> newChildren;
try {
newChildren = h.listChildren(e).stream().filter(dataStoreEntryRef -> dataStoreEntryRef != null && dataStoreEntryRef.get() != null).toList();
newChildren = h.listChildren(e).stream()
.filter(dataStoreEntryRef -> dataStoreEntryRef != null && dataStoreEntryRef.get() != null)
.toList();
} catch (Exception ex) {
if (throwOnFail) {
throw ex;

View file

@ -101,9 +101,7 @@ public class DataStoreEntry extends StorageElement {
this.expanded = expanded;
this.color = color;
this.explicitOrder = explicitOrder;
this.provider = store != null
? DataStoreProviders.byStore(store)
: null;
this.provider = store != null ? DataStoreProviders.byStore(store) : null;
this.storePersistentStateNode = storePersistentState;
this.notes = notes;
}

View file

@ -1,14 +1,14 @@
package io.xpipe.app.storage;
import com.fasterxml.jackson.core.JacksonException;
import io.xpipe.app.ext.DataStorageExtensionProvider;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.core.process.OsType;
import io.xpipe.core.store.LocalStore;
import io.xpipe.core.util.JacksonMapper;
import com.fasterxml.jackson.core.JacksonException;
import lombok.Getter;
import org.apache.commons.io.FileUtils;
@ -53,19 +53,27 @@ public class StandardStorage extends DataStorage {
try {
FileUtils.forceMkdir(dir.toFile());
} catch (Exception e) {
ErrorEvent.fromThrowable("Unable to create vault directory", e).terminal(true).build().handle();
ErrorEvent.fromThrowable("Unable to create vault directory", e)
.terminal(true)
.build()
.handle();
}
try {
initSystemInfo();
} catch (Exception e) {
ErrorEvent.fromThrowable("Unable to load vault system info", e).build().handle();
ErrorEvent.fromThrowable("Unable to load vault system info", e)
.build()
.handle();
}
try {
initVaultKey();
} catch (Exception e) {
ErrorEvent.fromThrowable("Unable to load vault key file", e).terminal(true).build().handle();
ErrorEvent.fromThrowable("Unable to load vault key file", e)
.terminal(true)
.build()
.handle();
}
var storesDir = getStoresDir();
@ -76,7 +84,10 @@ public class StandardStorage extends DataStorage {
FileUtils.forceMkdir(categoriesDir.toFile());
FileUtils.forceMkdir(dataDir.toFile());
} catch (Exception e) {
ErrorEvent.fromThrowable("Unable to create vault directory", e).terminal(true).build().handle();
ErrorEvent.fromThrowable("Unable to create vault directory", e)
.terminal(true)
.build()
.handle();
}
try {

View file

@ -1,8 +1,5 @@
package io.xpipe.app.update;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.app.core.AppLogs;
import io.xpipe.app.core.AppProperties;
import io.xpipe.app.core.mode.OperationMode;
@ -17,6 +14,10 @@ import io.xpipe.core.store.FileNames;
import io.xpipe.core.store.LocalStore;
import io.xpipe.core.util.FailableRunnable;
import io.xpipe.core.util.XPipeInstallation;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.Getter;
import java.nio.file.Files;
@ -85,7 +86,8 @@ public class AppInstaller {
.toString();
var logsDir =
AppLogs.get().getSessionLogsDirectory().getParent().toString();
var logFile = FileNames.join(logsDir, "installer_" + file.getFileName().toString() + ".log");
var logFile = FileNames.join(
logsDir, "installer_" + file.getFileName().toString() + ".log");
var command = LocalShell.getShell().getShellDialect().equals(ShellDialects.CMD)
? getCmdCommand(file.toString(), logFile, exec)
: getPowershellCommand(file.toString(), logFile, exec);
@ -138,7 +140,9 @@ public class AppInstaller {
@Override
public void installLocal(Path file) throws Exception {
var start = AppPrefs.get() != null && AppPrefs.get().terminalType().getValue() != null && AppPrefs.get().terminalType().getValue().isAvailable();
var start = AppPrefs.get() != null
&& AppPrefs.get().terminalType().getValue() != null
&& AppPrefs.get().terminalType().getValue().isAvailable();
if (!start) {
return;
}
@ -174,7 +178,9 @@ public class AppInstaller {
@Override
public void installLocal(Path file) throws Exception {
var start = AppPrefs.get() != null && AppPrefs.get().terminalType().getValue() != null && AppPrefs.get().terminalType().getValue().isAvailable();
var start = AppPrefs.get() != null
&& AppPrefs.get().terminalType().getValue() != null
&& AppPrefs.get().terminalType().getValue().isAvailable();
if (!start) {
return;
}
@ -210,7 +216,9 @@ public class AppInstaller {
@Override
public void installLocal(Path file) throws Exception {
var start = AppPrefs.get() != null && AppPrefs.get().terminalType().getValue() != null && AppPrefs.get().terminalType().getValue().isAvailable();
var start = AppPrefs.get() != null
&& AppPrefs.get().terminalType().getValue() != null
&& AppPrefs.get().terminalType().getValue().isAvailable();
if (!start) {
return;
}

View file

@ -3,7 +3,9 @@ package io.xpipe.app.update;
import io.xpipe.app.core.AppCache;
import io.xpipe.app.core.AppProperties;
import io.xpipe.app.issue.ErrorEvent;
import javafx.scene.layout.Region;
import org.kohsuke.github.GHRelease;
import java.nio.file.Files;

View file

@ -7,11 +7,13 @@ import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.app.util.BooleanScope;
import io.xpipe.app.util.ThreadHelper;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.layout.Region;
import lombok.Builder;
import lombok.Getter;
import lombok.Value;
@ -70,7 +72,9 @@ public abstract class UpdateHandler {
}
// Check if file has been deleted
if (preparedUpdate.getValue() != null && preparedUpdate.getValue().getFile() != null && !Files.exists(preparedUpdate.getValue().getFile())) {
if (preparedUpdate.getValue() != null
&& preparedUpdate.getValue().getFile() != null
&& !Files.exists(preparedUpdate.getValue().getFile())) {
preparedUpdate.setValue(null);
}

View file

@ -1,6 +1,7 @@
package io.xpipe.app.util;
import io.xpipe.app.fxcomps.util.LabelGraphic;
import javafx.application.Platform;
import javafx.geometry.Side;
import javafx.scene.Node;

View file

@ -5,6 +5,7 @@ import io.xpipe.app.fxcomps.util.BindingsHelper;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.core.process.ShellDialects;
import io.xpipe.core.process.ShellStoreState;
import javafx.beans.value.ObservableValue;
public class DataStoreFormatter {

View file

@ -31,9 +31,11 @@ public class DesktopHelper {
public static Path getDownloadsDirectory() throws Exception {
if (OsType.getLocal() == OsType.WINDOWS) {
return Path.of(LocalShell.getLocalPowershell()
.executeSimpleStringCommand("(New-Object -ComObject Shell.Application).NameSpace('shell:Downloads').Self.Path"));
.executeSimpleStringCommand(
"(New-Object -ComObject Shell.Application).NameSpace('shell:Downloads').Self.Path"));
} else if (OsType.getLocal() == OsType.LINUX) {
try (var cmd = LocalShell.getShell().command("xdg-user-dir DOWNLOAD").start()) {
try (var cmd =
LocalShell.getShell().command("xdg-user-dir DOWNLOAD").start()) {
var read = cmd.readStdoutDiscardErr();
var exit = cmd.getExitCode();
if (exit == 0) {

View file

@ -249,9 +249,12 @@ public class FileBridge {
var newDate = getLastModified();
// The size check is intended for cases in which editors first clear a file prior to writing it
// In that case, multiple watch events are sent. If these happened very fast, it might be possible that
// the modified time is the same for both write operations due to the file system modified time resolution being limited
// We then can't identify changes purely based on the modified time, so the file size is the next best option
// This might result in double change detection in rare cases, but that is irrelevant as it prevents files from being blanked
// the modified time is the same for both write operations due to the file system modified time resolution
// being limited
// We then can't identify changes purely based on the modified time, so the file size is the next best
// option
// This might result in double change detection in rare cases, but that is irrelevant as it prevents files
// from being blanked
var changed = !newDate.equals(lastModified) || newSize > lastSize;
lastSize = newSize;
lastModified = newDate;

View file

@ -1,10 +1,11 @@
package io.xpipe.app.util;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.core.util.XPipeInstallation;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.NativeLong;
import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.core.util.XPipeInstallation;
import java.util.Map;
import java.util.Optional;
@ -17,8 +18,15 @@ public class NativeBridge {
public static Optional<MacOsLibrary> getMacOsLibrary() {
if (macOsLibrary == null && !loadingFailed) {
try {
System.setProperty("jna.library.path", XPipeInstallation.getCurrentInstallationBasePath()
.resolve("Contents").resolve("runtime").resolve("Contents").resolve("Home").resolve("lib").toString());
System.setProperty(
"jna.library.path",
XPipeInstallation.getCurrentInstallationBasePath()
.resolve("Contents")
.resolve("runtime")
.resolve("Contents")
.resolve("Home")
.resolve("lib")
.toString());
var l = Native.load("xpipe_bridge", MacOsLibrary.class, Map.of());
macOsLibrary = l;
} catch (Throwable t) {

View file

@ -6,7 +6,9 @@ import io.xpipe.app.issue.ErrorEvent;
import io.xpipe.app.issue.TrackEvent;
import io.xpipe.app.prefs.AppPrefs;
import io.xpipe.core.process.OsType;
import javafx.application.Platform;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.SystemUtils;
@ -121,7 +123,8 @@ public enum PlatformState {
}
if (SystemUtils.IS_OS_WINDOWS && ModifiedStage.mergeFrame()) {
// This is primarily intended to fix Windows unified stage transparency issues (https://bugs.openjdk.org/browse/JDK-8329382)
// This is primarily intended to fix Windows unified stage transparency issues
// (https://bugs.openjdk.org/browse/JDK-8329382)
System.setProperty("prism.forceUploadingPainter", "true");
}
@ -152,7 +155,7 @@ public enum PlatformState {
// Platform initialization has failed in this case
PlatformState.setCurrent(PlatformState.EXITED);
TrackEvent.error(t.getMessage());
lastError =t;
lastError = t;
return;
}
}

View file

@ -2,6 +2,7 @@ package io.xpipe.beacon.api;
import io.xpipe.beacon.BeaconInterface;
import io.xpipe.core.store.DataStore;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;

View file

@ -1,6 +1,7 @@
package io.xpipe.beacon.api;
import io.xpipe.beacon.BeaconInterface;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;

View file

@ -1,6 +1,7 @@
package io.xpipe.beacon.api;
import io.xpipe.beacon.BeaconInterface;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;

View file

@ -1,6 +1,7 @@
package io.xpipe.beacon.api;
import io.xpipe.beacon.BeaconInterface;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;

View file

@ -1,6 +1,7 @@
package io.xpipe.beacon.api;
import io.xpipe.beacon.BeaconInterface;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;

View file

@ -1,6 +1,7 @@
package io.xpipe.beacon.api;
import io.xpipe.beacon.BeaconInterface;
import lombok.Builder;
import lombok.NonNull;
import lombok.Value;

View file

@ -26,12 +26,16 @@ public class DaemonVersionExchange extends BeaconInterface<DaemonVersionExchange
@NonNull
String version;
@NonNull
String canonicalVersion;
@NonNull
String buildVersion;
@NonNull
String jvmVersion;
@NonNull
Boolean pro;
}

View file

@ -1,8 +1,9 @@
package io.xpipe.core.store;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.xpipe.core.process.CommandBuilder;
import io.xpipe.core.process.ShellControl;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Getter;
import java.io.InputStream;

View file

@ -60,7 +60,8 @@ public interface NetworkTunnelStore extends DataStore {
default NetworkTunnelSession sessionChain(int local, int remotePort) throws Exception {
if (!isLocallyTunneable()) {
throw new IllegalStateException("Unable to create tunnel chain as one intermediate system does not support tunneling");
throw new IllegalStateException(
"Unable to create tunnel chain as one intermediate system does not support tunneling");
}
var running = new AtomicBoolean();

View file

@ -1,13 +1,5 @@
package io.xpipe.core.util;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.module.SimpleModule;
import io.xpipe.core.dialog.BaseQueryElement;
import io.xpipe.core.dialog.BusyElement;
import io.xpipe.core.dialog.ChoiceElement;
@ -19,6 +11,15 @@ import io.xpipe.core.store.FilePath;
import io.xpipe.core.store.LocalStore;
import io.xpipe.core.store.StorePath;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.jsontype.NamedType;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Path;

View file

@ -3,8 +3,8 @@ package io.xpipe.core.util;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.Getter;
@ -91,7 +91,6 @@ public class JacksonMapper {
return INSTANCE;
}
public static ObjectMapper getCensored() {
if (!JacksonMapper.isInit()) {
return BASE;
@ -103,13 +102,18 @@ public class JacksonMapper {
public void setupModule(SetupContext context) {
addSerializer(SecretValue.class, new JsonSerializer<>() {
@Override
public void serialize(SecretValue value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
public void serialize(SecretValue value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
gen.writeString("<secret>");
}
@Override
public void serializeWithType(SecretValue value, JsonGenerator gen, SerializerProvider serializers, TypeSerializer typeSer) throws
IOException {
public void serializeWithType(
SecretValue value,
JsonGenerator gen,
SerializerProvider serializers,
TypeSerializer typeSer)
throws IOException {
gen.writeString("<secret>");
}
});

View file

@ -48,6 +48,7 @@ public class OpenFileDefaultAction implements LeafAction {
@Override
public boolean isApplicable(OpenFileSystemModel model, List<BrowserEntry> entries) {
return model.getFileList().getEditing().getValue() == null && entries.stream().allMatch(entry -> entry.getRawFileEntry().getKind() == FileKind.FILE);
return model.getFileList().getEditing().getValue() == null
&& entries.stream().allMatch(entry -> entry.getRawFileEntry().getKind() == FileKind.FILE);
}
}

View file

@ -91,7 +91,8 @@ public class RunScriptAction implements BrowserAction, BranchAction {
var content = e.getValue().assemble(sc);
var script = ScriptHelper.createExecScript(sc, content);
try {
sc.executeSimpleCommand(sc.getShellDialect().runScriptCommand(sc, script.toString()) + " " + args);
sc.executeSimpleCommand(
sc.getShellDialect().runScriptCommand(sc, script.toString()) + " " + args);
} catch (Exception ex) {
throw ErrorEvent.expected(ex);
}

View file

@ -78,7 +78,8 @@ public class ScriptGroupStoreProvider implements EnabledStoreProvider, DataStore
@Override
public ObservableValue<String> informationString(StoreSection section) {
ScriptGroupStore scriptStore = section.getWrapper().getEntry().getStore().asNeeded();
ScriptGroupStore scriptStore =
section.getWrapper().getEntry().getStore().asNeeded();
return new SimpleStringProperty(scriptStore.getDescription());
}

View file

@ -87,7 +87,8 @@ public abstract class ScriptStore extends JacksonizedValue implements DataStore,
}
return pc;
} catch (StackOverflowError t) {
throw ErrorEvent.expected(new RuntimeException("Unable to set up scripts. Is there a circular script dependency?", t));
throw ErrorEvent.expected(
new RuntimeException("Unable to set up scripts. Is there a circular script dependency?", t));
} catch (Throwable t) {
throw new RuntimeException("Unable to set up scripts", t);
}

View file

@ -208,7 +208,8 @@ public class SimpleScriptStoreProvider implements EnabledParentStoreProvider, Da
@Override
public ObservableValue<String> informationString(StoreSection section) {
SimpleScriptStore scriptStore = section.getWrapper().getEntry().getStore().asNeeded();
SimpleScriptStore scriptStore =
section.getWrapper().getEntry().getStore().asNeeded();
return new SimpleStringProperty((scriptStore.getMinimumDialect() != null
? scriptStore.getMinimumDialect().getDisplayName() + " "
: "")

View file

@ -67,16 +67,22 @@ public abstract class AbstractServiceGroupStoreProvider implements DataStoreProv
@Override
public ObservableValue<String> informationString(StoreSection section) {
return Bindings.createStringBinding(() -> {
var all = section.getAllChildren().getList();
var shown = section.getShownChildren().getList();
if (shown.size() == 0) {
return null;
}
return Bindings.createStringBinding(
() -> {
var all = section.getAllChildren().getList();
var shown = section.getShownChildren().getList();
if (shown.size() == 0) {
return null;
}
var string = all.size() == shown.size() ? all.size() : shown.size() + "/" + all.size();
return all.size() > 0 ? (all.size() == 1 ? AppI18n.get("hasService", string) : AppI18n.get("hasServices", string)) : AppI18n.get("noServices");
}, section.getShownChildren().getList(), section.getAllChildren().getList(), AppPrefs.get().language());
var string = all.size() == shown.size() ? all.size() : shown.size() + "/" + all.size();
return all.size() > 0
? (all.size() == 1 ? AppI18n.get("hasService", string) : AppI18n.get("hasServices", string))
: AppI18n.get("noServices");
},
section.getShownChildren().getList(),
section.getAllChildren().getList(),
AppPrefs.get().language());
}
@Override

View file

@ -22,5 +22,4 @@ public class CustomServiceGroupStore extends AbstractServiceGroupStore<NetworkTu
super.checkComplete();
Validators.isType(getParent(), NetworkTunnelStore.class);
}
}

View file

@ -1,12 +1,13 @@
package io.xpipe.ext.base.service;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.xpipe.app.storage.DataStoreEntry;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.util.FixedHierarchyStore;
import io.xpipe.app.util.Validators;
import io.xpipe.core.store.DataStore;
import io.xpipe.core.store.FixedChildStore;
import com.fasterxml.jackson.annotation.JsonTypeName;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
@ -26,7 +27,7 @@ public class FixedServiceGroupStore extends AbstractServiceGroupStore<FixedServi
@Override
public void checkComplete() throws Throwable {
super.checkComplete();
Validators.isType(getParent(),FixedServiceCreatorStore.class);
Validators.isType(getParent(), FixedServiceCreatorStore.class);
}
@Override

View file

@ -3,6 +3,7 @@ package io.xpipe.ext.base.service;
import io.xpipe.app.comp.store.StoreSection;
import io.xpipe.app.storage.DataStorage;
import io.xpipe.app.storage.DataStoreEntry;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;

View file

@ -4,6 +4,7 @@ import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.core.store.DataStore;
import javafx.beans.value.ObservableValue;
import java.util.List;

View file

@ -4,7 +4,9 @@ import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.util.Hyperlinks;
import javafx.beans.value.ObservableValue;
import lombok.Value;
public class ServiceOpenHttpAction implements ActionProvider {
@ -44,7 +46,7 @@ public class ServiceOpenHttpAction implements ActionProvider {
}
};
}
@Value
static class Action implements ActionProvider.Action {

View file

@ -4,7 +4,9 @@ import io.xpipe.app.core.AppI18n;
import io.xpipe.app.ext.ActionProvider;
import io.xpipe.app.storage.DataStoreEntryRef;
import io.xpipe.app.util.Hyperlinks;
import javafx.beans.value.ObservableValue;
import lombok.Value;
public class ServiceOpenHttpsAction implements ActionProvider {
@ -44,7 +46,7 @@ public class ServiceOpenHttpsAction implements ActionProvider {
}
};
}
@Value
static class Action implements ActionProvider.Action {

View file

@ -62,7 +62,10 @@ open module io.xpipe.ext.base {
UnzipAction,
JavapAction,
JarAction;
provides ActionProvider with StoreStopAction, StoreStartAction, StorePauseAction,
provides ActionProvider with
StoreStopAction,
StoreStartAction,
StorePauseAction,
ServiceOpenAction,
ServiceOpenHttpAction,
ServiceOpenHttpsAction,