From 8ba2540b35b1e981f2f231a468564565b4b02693 Mon Sep 17 00:00:00 2001 From: Jan-Peter Klein Date: Mon, 11 Sep 2023 14:23:31 +0200 Subject: [PATCH] implemented volume type selection in 'vault options mount' --- .../vaultoptions/MountOptionsController.java | 126 ++++++++++++++++-- .../resources/fxml/vault_options_mount.fxml | 29 +++- 2 files changed, 140 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/cryptomator/ui/vaultoptions/MountOptionsController.java b/src/main/java/org/cryptomator/ui/vaultoptions/MountOptionsController.java index 5eeab43e0..4e97e7fd5 100644 --- a/src/main/java/org/cryptomator/ui/vaultoptions/MountOptionsController.java +++ b/src/main/java/org/cryptomator/ui/vaultoptions/MountOptionsController.java @@ -1,18 +1,26 @@ package org.cryptomator.ui.vaultoptions; import com.google.common.base.Strings; +import dagger.Lazy; +import org.cryptomator.common.ObservableUtil; import org.cryptomator.common.mount.ActualMountService; +import org.cryptomator.common.mount.MountModule; import org.cryptomator.common.mount.WindowsDriveLetters; +import org.cryptomator.common.settings.Settings; import org.cryptomator.common.settings.VaultSettings; import org.cryptomator.common.vaults.Vault; import org.cryptomator.integrations.mount.MountCapability; +import org.cryptomator.integrations.mount.MountService; import org.cryptomator.ui.common.FxController; -import org.cryptomator.ui.fxapp.FxApplicationWindows; -import org.cryptomator.ui.preferences.SelectedPreferencesTab; import javax.inject.Inject; +import javax.inject.Named; +import javafx.application.Application; +import javafx.beans.binding.Bindings; +import javafx.beans.binding.BooleanExpression; import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; +import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.ChoiceBox; import javafx.scene.control.RadioButton; @@ -26,16 +34,25 @@ import java.io.File; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; +import java.util.List; +import java.util.Optional; import java.util.ResourceBundle; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; @VaultOptionsScoped public class MountOptionsController implements FxController { + private static final String DOCS_MOUNTING_URL = "https://docs.cryptomator.org/en/1.7/desktop/volume-type/"; + private static final int MIN_PORT = 1024; + private static final int MAX_PORT = 65535; + private final Stage window; private final VaultSettings vaultSettings; private final WindowsDriveLetters windowsDriveLetters; private final ResourceBundle resourceBundle; + private final Lazy application; + private final Settings settings; private final ObservableValue defaultMountFlags; private final ObservableValue mountpointDirSupported; @@ -43,7 +60,10 @@ public class MountOptionsController implements FxController { private final ObservableValue readOnlySupported; private final ObservableValue mountFlagsSupported; private final ObservableValue directoryPath; - private final FxApplicationWindows applicationWindows; + private final List mountProviders; + private final ObservableValue selectedMountService; + private final ObservableValue fuseRestartRequired; + private final BooleanExpression loopbackPortSupported; //-- FXML objects -- @@ -56,9 +76,21 @@ public class MountOptionsController implements FxController { public RadioButton mountPointDirBtn; public TextField directoryPathField; public ChoiceBox driveLetterSelection; + public ChoiceBox volumeTypeChoiceBox; + public TextField loopbackPortField; + public Button loopbackPortApplyButton; + @Inject - MountOptionsController(@VaultOptionsWindow Stage window, @VaultOptionsWindow Vault vault, ObservableValue mountService, WindowsDriveLetters windowsDriveLetters, ResourceBundle resourceBundle, FxApplicationWindows applicationWindows) { + MountOptionsController(@VaultOptionsWindow Stage window, // + @VaultOptionsWindow Vault vault, // + ObservableValue mountService, // + WindowsDriveLetters windowsDriveLetters, // + ResourceBundle resourceBundle, // + Lazy application, + Settings settings, // + List mountProviders, // + @Named("FUPFMS") AtomicReference firstUsedProblematicFuseMountService) { this.window = window; this.vaultSettings = vault.getVaultSettings(); this.windowsDriveLetters = windowsDriveLetters; @@ -75,7 +107,17 @@ public class MountOptionsController implements FxController { this.mountFlagsSupported = mountService.map(as -> as.service().hasCapability(MountCapability.MOUNT_FLAGS)); this.readOnlySupported = mountService.map(as -> as.service().hasCapability(MountCapability.READ_ONLY)); this.directoryPath = vault.getVaultSettings().mountPoint.map(p -> isDriveLetter(p) ? null : p.toString()); - this.applicationWindows = applicationWindows; + this.application = application; + this.settings = settings; + this.mountProviders = mountProviders; + var fallbackProvider = mountProviders.stream().findFirst().orElse(null); + this.selectedMountService = ObservableUtil.mapWithDefault(settings.mountService, serviceName -> mountProviders.stream().filter(s -> s.getClass().getName().equals(serviceName)).findFirst().orElse(fallbackProvider), fallbackProvider); + this.fuseRestartRequired = selectedMountService.map(s -> {// + return firstUsedProblematicFuseMountService.get() != null // + && MountModule.isProblematicFuseService(s) // + && !firstUsedProblematicFuseMountService.get().equals(s); + }); + this.loopbackPortSupported = BooleanExpression.booleanExpression(selectedMountService.map(s -> s.hasCapability(MountCapability.LOOPBACK_PORT))); } @FXML @@ -106,11 +148,21 @@ public class MountOptionsController implements FxController { mountPointToggleGroup.selectToggle(mountPointDirBtn); } mountPointToggleGroup.selectedToggleProperty().addListener(this::selectedToggleChanged); - } - @FXML - public void openVolumePreferences() { - applicationWindows.showPreferencesWindow(SelectedPreferencesTab.VOLUME); + volumeTypeChoiceBox.getItems().add(null); + volumeTypeChoiceBox.getItems().addAll(mountProviders); + volumeTypeChoiceBox.setConverter(new MountServiceConverter()); + boolean autoSelected = settings.mountService.get() == null; + volumeTypeChoiceBox.getSelectionModel().select(autoSelected ? null : selectedMountService.getValue()); + volumeTypeChoiceBox.valueProperty().addListener((observableValue, oldProvider, newProvider) -> { + var toSet = Optional.ofNullable(newProvider).map(nP -> nP.getClass().getName()).orElse(null); + settings.mountService.set(toSet); + }); + + loopbackPortField.setText(String.valueOf(settings.port.get())); + loopbackPortApplyButton.visibleProperty().bind(settings.port.asString().isNotEqualTo(loopbackPortField.textProperty())); + loopbackPortApplyButton.disableProperty().bind(Bindings.createBooleanBinding(this::validateLoopbackPort, loopbackPortField.textProperty()).not()); + } @FXML @@ -229,6 +281,26 @@ public class MountOptionsController implements FxController { } + public void openDocs() { + application.get().getHostServices().showDocument(DOCS_MOUNTING_URL); + } + + private boolean validateLoopbackPort() { + try { + int port = Integer.parseInt(loopbackPortField.getText()); + return port == 0 // choose port automatically + || port >= MIN_PORT && port <= MAX_PORT; // port within range + } catch (NumberFormatException e) { + return false; + } + } + + public void doChangeLoopbackPort() { + if (validateLoopbackPort()) { + settings.port.set(Integer.parseInt(loopbackPortField.getText())); + } + } + //@formatter:off private static class NoDirSelectedException extends Exception {} //@formatter:on @@ -274,4 +346,40 @@ public class MountOptionsController implements FxController { public String getDirectoryPath() { return directoryPath.getValue(); } + + public ObservableValue fuseRestartRequiredProperty() { + return fuseRestartRequired; + } + + public boolean getFuseRestartRequired() { + return fuseRestartRequired.getValue(); + } + + public BooleanExpression loopbackPortSupportedProperty() { + return loopbackPortSupported; + } + + public boolean isLoopbackPortSupported() { + return loopbackPortSupported.get(); + } + + //Helpers + /* Helpers */ + + private class MountServiceConverter extends StringConverter { + + @Override + public String toString(MountService provider) { + if (provider == null) { + return resourceBundle.getString("preferences.volume.type.automatic"); + } else { + return provider.displayName(); + } + } + + @Override + public MountService fromString(String string) { + throw new UnsupportedOperationException(); + } + } } diff --git a/src/main/resources/fxml/vault_options_mount.fxml b/src/main/resources/fxml/vault_options_mount.fxml index 762d36d27..921747132 100644 --- a/src/main/resources/fxml/vault_options_mount.fxml +++ b/src/main/resources/fxml/vault_options_mount.fxml @@ -1,6 +1,7 @@ + @@ -12,7 +13,7 @@ - + - - + + + +