소스 검색

implemented volume type selection in 'vault options mount'

Jan-Peter Klein 1 년 전
부모
커밋
8ba2540b35
2개의 변경된 파일140개의 추가작업 그리고 15개의 파일을 삭제
  1. 117 9
      src/main/java/org/cryptomator/ui/vaultoptions/MountOptionsController.java
  2. 23 6
      src/main/resources/fxml/vault_options_mount.fxml

+ 117 - 9
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> application;
+	private final Settings settings;
 
 	private final ObservableValue<String> defaultMountFlags;
 	private final ObservableValue<Boolean> mountpointDirSupported;
@@ -43,7 +60,10 @@ public class MountOptionsController implements FxController {
 	private final ObservableValue<Boolean> readOnlySupported;
 	private final ObservableValue<Boolean> mountFlagsSupported;
 	private final ObservableValue<String> directoryPath;
-	private final FxApplicationWindows applicationWindows;
+	private final List<MountService> mountProviders;
+	private final ObservableValue<MountService> selectedMountService;
+	private final ObservableValue<Boolean> fuseRestartRequired;
+	private final BooleanExpression loopbackPortSupported;
 
 
 	//-- FXML objects --
@@ -56,9 +76,21 @@ public class MountOptionsController implements FxController {
 	public RadioButton mountPointDirBtn;
 	public TextField directoryPathField;
 	public ChoiceBox<Path> driveLetterSelection;
+	public ChoiceBox<MountService> volumeTypeChoiceBox;
+	public TextField loopbackPortField;
+	public Button loopbackPortApplyButton;
+
 
 	@Inject
-	MountOptionsController(@VaultOptionsWindow Stage window, @VaultOptionsWindow Vault vault, ObservableValue<ActualMountService> mountService, WindowsDriveLetters windowsDriveLetters, ResourceBundle resourceBundle, FxApplicationWindows applicationWindows) {
+	MountOptionsController(@VaultOptionsWindow Stage window, //
+						   @VaultOptionsWindow Vault vault, //
+						   ObservableValue<ActualMountService> mountService, //
+						   WindowsDriveLetters windowsDriveLetters, //
+						   ResourceBundle resourceBundle, //
+						   Lazy<Application> application,
+						   Settings settings, //
+						   List<MountService> mountProviders, //
+						   @Named("FUPFMS") AtomicReference<MountService> 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<Boolean> 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<MountService> {
+
+		@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();
+		}
+	}
 }

+ 23 - 6
src/main/resources/fxml/vault_options_mount.fxml

@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 <?import org.cryptomator.ui.controls.FontAwesome5IconView?>
+<?import org.cryptomator.ui.controls.NumericTextField?>
 <?import javafx.geometry.Insets?>
 <?import javafx.scene.control.Button?>
 <?import javafx.scene.control.CheckBox?>
@@ -12,7 +13,7 @@
 <?import javafx.scene.control.ToggleGroup?>
 <?import javafx.scene.layout.HBox?>
 <?import javafx.scene.layout.VBox?>
-<?import javafx.scene.text.TextFlow?>
+<?import javafx.scene.control.Tooltip?>
 <VBox xmlns:fx="http://javafx.com/fxml"
 	  xmlns="http://javafx.com/javafx"
 	  fx:controller="org.cryptomator.ui.vaultoptions.MountOptionsController"
@@ -24,11 +25,27 @@
 		<Insets topRightBottomLeft="12"/>
 	</padding>
 	<children>
-		<TextFlow>
-			<Label text="%vaultOptions.mount.info"/>
-			<Label text=" "/>
-			<Hyperlink styleClass="hyperlink-underline" text="%vaultOptions.mount.linkToPreferences" onAction="#openVolumePreferences" wrapText="true"/>
-		</TextFlow>
+		<HBox spacing="12" alignment="CENTER_LEFT">
+			<Label text="%preferences.volume.type"/>
+			<ChoiceBox fx:id="volumeTypeChoiceBox"/>
+			<Hyperlink contentDisplay="GRAPHIC_ONLY" onAction="#openDocs">
+				<graphic>
+					<FontAwesome5IconView glyph="QUESTION_CIRCLE" styleClass="glyph-icon-muted"/>
+				</graphic>
+				<tooltip>
+					<Tooltip text="%preferences.volume.docsTooltip" showDelay="100ms"/>
+				</tooltip>
+			</Hyperlink>
+		</HBox>
+
+		<Label styleClass="label-red" text="%preferences.volume.fuseRestartRequired" visible="${controller.fuseRestartRequired}" managed="${controller.fuseRestartRequired}"/>
+
+		<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.loopbackPortSupported}" managed="${controller.loopbackPortSupported}">
+			<Label text="%preferences.volume.tcp.port"/>
+			<NumericTextField fx:id="loopbackPortField"/>
+			<Button text="%generic.button.apply" fx:id="loopbackPortApplyButton" onAction="#doChangeLoopbackPort"/>
+		</HBox>
+
 		<CheckBox fx:id="readOnlyCheckbox" text="%vaultOptions.mount.readonly" visible="${controller.readOnlySupported}" managed="${controller.readOnlySupported}"/>
 
 		<VBox visible="${controller.mountFlagsSupported}" managed="${controller.mountFlagsSupported}">