mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-20 19:51:27 +00:00
Refactor mount point setting and controller :
* remove CustomMountPath, winDriveLetter and usesCustomMountPath * new property mountPoint (can be null) * differentiation between driveLetter and directory happens in controller
This commit is contained in:
@@ -5,7 +5,6 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.common.mount;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
|
||||
@@ -13,8 +12,10 @@ import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.StreamSupport;
|
||||
@@ -22,64 +23,48 @@ import java.util.stream.StreamSupport;
|
||||
@Singleton
|
||||
public final class WindowsDriveLetters {
|
||||
|
||||
private static final Set<String> A_TO_Z;
|
||||
private static final Set<Path> A_TO_Z;
|
||||
|
||||
static {
|
||||
try (IntStream stream = IntStream.rangeClosed('A', 'Z')) {
|
||||
A_TO_Z = stream.mapToObj(i -> String.valueOf((char) i)).collect(ImmutableSet.toImmutableSet());
|
||||
}
|
||||
var sortedSet = new TreeSet<Path>();
|
||||
IntStream.rangeClosed('A', 'Z').mapToObj(i -> Path.of((char) i + ":\\")).forEach(sortedSet::add);
|
||||
A_TO_Z = Collections.unmodifiableSet(sortedSet);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public WindowsDriveLetters() {
|
||||
}
|
||||
|
||||
public Set<String> getAllDriveLetters() {
|
||||
public Set<Path> getAll() {
|
||||
return A_TO_Z;
|
||||
}
|
||||
|
||||
public Set<String> getOccupiedDriveLetters() {
|
||||
public Set<Path> getOccupied() {
|
||||
if (!SystemUtils.IS_OS_WINDOWS) {
|
||||
return Set.of();
|
||||
} else {
|
||||
Iterable<Path> rootDirs = FileSystems.getDefault().getRootDirectories();
|
||||
return StreamSupport.stream(rootDirs.spliterator(), false).map(p -> p.toString().substring(0, 1)).collect(Collectors.toSet());
|
||||
return StreamSupport.stream(rootDirs.spliterator(), false).collect(Collectors.toUnmodifiableSet());
|
||||
}
|
||||
}
|
||||
|
||||
public Set<String> getAvailableDriveLetters() {
|
||||
return Sets.difference(getAllDriveLetters(), getOccupiedDriveLetters());
|
||||
}
|
||||
|
||||
public Optional<String> getAvailableDriveLetter() {
|
||||
return getAvailableDriveLetters().stream().findFirst();
|
||||
}
|
||||
|
||||
public Optional<Path> getAvailableDriveLetterPath() {
|
||||
return getAvailableDriveLetter().map(this::toPath);
|
||||
public Set<Path> getAvailable() {
|
||||
return Sets.difference(getAll(), getOccupied());
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips A and B and only returns them if all other are occupied.
|
||||
* Skips A and B and only returns them if all others are occupied.
|
||||
*
|
||||
* @return an Optional containing either the letter of a free drive letter or empty, if none is available
|
||||
*/
|
||||
public Optional<String> getDesiredAvailableDriveLetter() {
|
||||
var availableDriveLetters = getAvailableDriveLetters();
|
||||
var optString = availableDriveLetters.stream().filter(s -> !(s.equals("A") || s.equals("B"))).findFirst();
|
||||
public Optional<Path> getFirstDesiredAvailable() {
|
||||
var availableDriveLetters = getAvailable();
|
||||
var optString = availableDriveLetters.stream().filter(this::notAOrB).findFirst();
|
||||
return optString.or(() -> availableDriveLetters.stream().findFirst());
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips A and B and only returns them if all other are occupied.
|
||||
*
|
||||
* @return an Optional containing either the path to a free drive letter or empty, if none is available
|
||||
*/
|
||||
public Optional<Path> getDesiredAvailableDriveLetterPath() {
|
||||
return getDesiredAvailableDriveLetter().map(this::toPath);
|
||||
private boolean notAOrB(Path driveLetter) {
|
||||
return !(Path.of("A:\\").equals(driveLetter) || Path.of("B:\\").equals(driveLetter));
|
||||
}
|
||||
|
||||
public Path toPath(String driveLetter) {
|
||||
return Path.of(driveLetter + ":\\");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
package org.cryptomator.common.settings;
|
||||
|
||||
import com.google.common.base.CharMatcher;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
|
||||
import javafx.beans.Observable;
|
||||
@@ -22,7 +21,6 @@ import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
@@ -32,7 +30,6 @@ public class VaultSettings {
|
||||
|
||||
public static final boolean DEFAULT_UNLOCK_AFTER_STARTUP = false;
|
||||
public static final boolean DEFAULT_REVEAL_AFTER_MOUNT = true;
|
||||
public static final boolean DEFAULT_USES_INDIVIDUAL_MOUNTPATH = false;
|
||||
public static final boolean DEFAULT_USES_READONLY_MODE = false;
|
||||
public static final String DEFAULT_MOUNT_FLAGS = "";
|
||||
public static final int DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH = -1;
|
||||
@@ -45,11 +42,8 @@ public class VaultSettings {
|
||||
private final String id;
|
||||
private final ObjectProperty<Path> path = new SimpleObjectProperty<>();
|
||||
private final StringProperty displayName = new SimpleStringProperty();
|
||||
private final StringProperty winDriveLetter = new SimpleStringProperty();
|
||||
private final BooleanProperty unlockAfterStartup = new SimpleBooleanProperty(DEFAULT_UNLOCK_AFTER_STARTUP);
|
||||
private final BooleanProperty revealAfterMount = new SimpleBooleanProperty(DEFAULT_REVEAL_AFTER_MOUNT);
|
||||
private final BooleanProperty useCustomMountPath = new SimpleBooleanProperty(DEFAULT_USES_INDIVIDUAL_MOUNTPATH);
|
||||
private final StringProperty customMountPath = new SimpleStringProperty();
|
||||
private final BooleanProperty usesReadOnlyMode = new SimpleBooleanProperty(DEFAULT_USES_READONLY_MODE);
|
||||
private final StringProperty mountFlags = new SimpleStringProperty(DEFAULT_MOUNT_FLAGS);
|
||||
private final IntegerProperty maxCleartextFilenameLength = new SimpleIntegerProperty(DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH);
|
||||
@@ -73,7 +67,7 @@ public class VaultSettings {
|
||||
}
|
||||
|
||||
Observable[] observables() {
|
||||
return new Observable[]{path, displayName, winDriveLetter, unlockAfterStartup, revealAfterMount, useCustomMountPath, customMountPath, usesReadOnlyMode, mountFlags, maxCleartextFilenameLength, actionAfterUnlock, autoLockWhenIdle, autoLockIdleSeconds};
|
||||
return new Observable[]{actionAfterUnlock, autoLockIdleSeconds, autoLockWhenIdle, displayName, maxCleartextFilenameLength, mountFlags, mountPoint, path, revealAfterMount, unlockAfterStartup, usesReadOnlyMode};
|
||||
}
|
||||
|
||||
public static VaultSettings withRandomId() {
|
||||
@@ -117,18 +111,6 @@ public class VaultSettings {
|
||||
return mountName;
|
||||
}
|
||||
|
||||
public StringProperty winDriveLetter() {
|
||||
return winDriveLetter;
|
||||
}
|
||||
|
||||
public Optional<String> getWinDriveLetter() {
|
||||
String current = this.winDriveLetter.get();
|
||||
if (!Strings.isNullOrEmpty(current)) {
|
||||
return Optional.of(current);
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
public BooleanProperty unlockAfterStartup() {
|
||||
return unlockAfterStartup;
|
||||
}
|
||||
@@ -137,20 +119,12 @@ public class VaultSettings {
|
||||
return revealAfterMount;
|
||||
}
|
||||
|
||||
public BooleanProperty useCustomMountPath() {
|
||||
return useCustomMountPath;
|
||||
public Path getMountPoint() {
|
||||
return mountPoint.get();
|
||||
}
|
||||
|
||||
public StringProperty customMountPath() {
|
||||
return customMountPath;
|
||||
}
|
||||
|
||||
public Optional<String> getCustomMountPath() {
|
||||
if (useCustomMountPath.get()) {
|
||||
return Optional.ofNullable(Strings.emptyToNull(customMountPath.get()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
public ObjectProperty<Path> mountPoint() {
|
||||
return mountPoint;
|
||||
}
|
||||
|
||||
public BooleanProperty usesReadOnlyMode() {
|
||||
@@ -196,12 +170,4 @@ public class VaultSettings {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public Path getMountPoint() {
|
||||
return mountPoint.get();
|
||||
}
|
||||
|
||||
public ObjectProperty<Path> mountPointProperty() {
|
||||
return mountPoint;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
package org.cryptomator.common.settings;
|
||||
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonToken;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
class VaultSettingsJsonAdapter {
|
||||
@@ -22,11 +25,10 @@ class VaultSettingsJsonAdapter {
|
||||
out.name("id").value(value.getId());
|
||||
out.name("path").value(value.path().get().toString());
|
||||
out.name("displayName").value(value.displayName().get());
|
||||
out.name("winDriveLetter").value(value.winDriveLetter().get());
|
||||
out.name("unlockAfterStartup").value(value.unlockAfterStartup().get());
|
||||
out.name("revealAfterMount").value(value.revealAfterMount().get());
|
||||
out.name("useCustomMountPath").value(value.useCustomMountPath().get());
|
||||
out.name("customMountPath").value(value.customMountPath().get());
|
||||
var mountPoint = value.mountPoint().get();
|
||||
out.name("mountPoint").value(mountPoint != null ? mountPoint.toAbsolutePath().toString() : null);
|
||||
out.name("usesReadOnlyMode").value(value.usesReadOnlyMode().get());
|
||||
out.name("mountFlags").value(value.mountFlags().get());
|
||||
out.name("maxCleartextFilenameLength").value(value.maxCleartextFilenameLength().get());
|
||||
@@ -36,18 +38,18 @@ class VaultSettingsJsonAdapter {
|
||||
out.endObject();
|
||||
}
|
||||
|
||||
//TODO: usesCustomMountPath, customMountPath and winDriveLetter removed
|
||||
// -> migration required
|
||||
public VaultSettings read(JsonReader in) throws IOException {
|
||||
String id = null;
|
||||
String path = null;
|
||||
String mountName = null; //see https://github.com/cryptomator/cryptomator/pull/1318
|
||||
String displayName = null;
|
||||
String customMountPath = null;
|
||||
String winDriveLetter = null;
|
||||
boolean unlockAfterStartup = VaultSettings.DEFAULT_UNLOCK_AFTER_STARTUP;
|
||||
boolean revealAfterMount = VaultSettings.DEFAULT_REVEAL_AFTER_MOUNT;
|
||||
boolean useCustomMountPath = VaultSettings.DEFAULT_USES_INDIVIDUAL_MOUNTPATH;
|
||||
boolean usesReadOnlyMode = VaultSettings.DEFAULT_USES_READONLY_MODE;
|
||||
String mountFlags = VaultSettings.DEFAULT_MOUNT_FLAGS;
|
||||
Path mountPoint = null;
|
||||
int maxCleartextFilenameLength = VaultSettings.DEFAULT_MAX_CLEARTEXT_FILENAME_LENGTH;
|
||||
WhenUnlocked actionAfterUnlock = VaultSettings.DEFAULT_ACTION_AFTER_UNLOCK;
|
||||
boolean autoLockWhenIdle = VaultSettings.DEFAULT_AUTOLOCK_WHEN_IDLE;
|
||||
@@ -61,13 +63,17 @@ class VaultSettingsJsonAdapter {
|
||||
case "path" -> path = in.nextString();
|
||||
case "mountName" -> mountName = in.nextString(); //see https://github.com/cryptomator/cryptomator/pull/1318
|
||||
case "displayName" -> displayName = in.nextString();
|
||||
case "winDriveLetter" -> winDriveLetter = in.nextString();
|
||||
case "unlockAfterStartup" -> unlockAfterStartup = in.nextBoolean();
|
||||
case "revealAfterMount" -> revealAfterMount = in.nextBoolean();
|
||||
case "usesIndividualMountPath", "useCustomMountPath" -> useCustomMountPath = in.nextBoolean();
|
||||
case "individualMountPath", "customMountPath" -> customMountPath = in.nextString();
|
||||
case "usesReadOnlyMode" -> usesReadOnlyMode = in.nextBoolean();
|
||||
case "mountFlags" -> mountFlags = in.nextString();
|
||||
case "mountPoint" -> {
|
||||
if (JsonToken.NULL == in.peek()) {
|
||||
in.nextNull();
|
||||
} else {
|
||||
mountPoint = parseMountPoint(in.nextString());
|
||||
}
|
||||
}
|
||||
case "maxCleartextFilenameLength" -> maxCleartextFilenameLength = in.nextInt();
|
||||
case "actionAfterUnlock" -> actionAfterUnlock = parseActionAfterUnlock(in.nextString());
|
||||
case "autoLockWhenIdle" -> autoLockWhenIdle = in.nextBoolean();
|
||||
@@ -87,20 +93,27 @@ class VaultSettingsJsonAdapter {
|
||||
vaultSettings.displayName().set(mountName);
|
||||
}
|
||||
vaultSettings.path().set(Paths.get(path));
|
||||
vaultSettings.winDriveLetter().set(winDriveLetter);
|
||||
vaultSettings.unlockAfterStartup().set(unlockAfterStartup);
|
||||
vaultSettings.revealAfterMount().set(revealAfterMount);
|
||||
vaultSettings.useCustomMountPath().set(useCustomMountPath);
|
||||
vaultSettings.customMountPath().set(customMountPath);
|
||||
vaultSettings.usesReadOnlyMode().set(usesReadOnlyMode);
|
||||
vaultSettings.mountFlags().set(mountFlags);
|
||||
vaultSettings.maxCleartextFilenameLength().set(maxCleartextFilenameLength);
|
||||
vaultSettings.actionAfterUnlock().set(actionAfterUnlock);
|
||||
vaultSettings.autoLockWhenIdle().set(autoLockWhenIdle);
|
||||
vaultSettings.autoLockIdleSeconds().set(autoLockIdleSeconds);
|
||||
vaultSettings.mountPoint().set(mountPoint);
|
||||
return vaultSettings;
|
||||
}
|
||||
|
||||
private Path parseMountPoint(String mountPoint) {
|
||||
try {
|
||||
return Path.of(mountPoint);
|
||||
} catch (InvalidPathException e) {
|
||||
LOG.warn("Invalid string as mount point. Defaulting to null.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private WhenUnlocked parseActionAfterUnlock(String actionAfterUnlockName) {
|
||||
try {
|
||||
return WhenUnlocked.valueOf(actionAfterUnlockName.toUpperCase());
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.google.common.base.Strings;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Constants;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.mount.WindowsDriveLetters;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystem;
|
||||
@@ -83,12 +84,13 @@ public class Vault {
|
||||
private final BooleanBinding needsMigration;
|
||||
private final BooleanBinding unknownError;
|
||||
private final ObjectBinding<Mountpoint> mountPoint;
|
||||
private final WindowsDriveLetters windowsDriveLetters;
|
||||
private final BooleanProperty showingStats;
|
||||
|
||||
private AtomicReference<MountHandle> mountHandle = new AtomicReference<>(null);
|
||||
|
||||
@Inject
|
||||
Vault(Environment env, Settings settings, VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, ObservableValue<MountService> mountService, VaultStats stats) {
|
||||
Vault(Environment env, Settings settings, VaultSettings vaultSettings, VaultConfigCache configCache, AtomicReference<CryptoFileSystem> cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty<Exception> lastKnownException, ObservableValue<MountService> mountService, VaultStats stats, WindowsDriveLetters windowsDriveLetters) {
|
||||
this.env = env;
|
||||
this.settings = settings;
|
||||
this.vaultSettings = vaultSettings;
|
||||
@@ -107,6 +109,7 @@ public class Vault {
|
||||
this.needsMigration = Bindings.createBooleanBinding(this::isNeedsMigration, state);
|
||||
this.unknownError = Bindings.createBooleanBinding(this::isUnknownError, state);
|
||||
this.mountPoint = Bindings.createObjectBinding(this::getMountPoint, state);
|
||||
this.windowsDriveLetters = windowsDriveLetters;
|
||||
this.showingStats = new SimpleBooleanProperty(false);
|
||||
}
|
||||
|
||||
@@ -178,7 +181,7 @@ public class Vault {
|
||||
if (mountProvider.hasCapability(MOUNT_TO_SYSTEM_CHOSEN_PATH)) {
|
||||
// no need to set a mount point
|
||||
} else if (mountProvider.hasCapability(MOUNT_AS_DRIVE_LETTER)) {
|
||||
// TODO find any free drive letter?
|
||||
builder.setMountpoint(windowsDriveLetters.getFirstDesiredAvailable().orElseThrow());
|
||||
} else if (mountProvider.hasCapability(MOUNT_WITHIN_EXISTING_PARENT)) {
|
||||
Files.createDirectories(defaultMountPointBase);
|
||||
builder.setMountpoint(defaultMountPointBase);
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
package org.cryptomator.ui.vaultoptions;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.vaults.Vault;
|
||||
import org.cryptomator.common.mount.WindowsDriveLetters;
|
||||
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 javax.inject.Inject;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.CheckBox;
|
||||
@@ -38,10 +36,13 @@ public class MountOptionsController implements FxController {
|
||||
|
||||
private final ObservableValue<Boolean> mountpointDirSupported;
|
||||
private final ObservableValue<Boolean> mountpointDriveLetterSupported;
|
||||
private final ObservableValue<Boolean> mountpointParentSupported; //TODO: use it in GUI
|
||||
private final ObservableValue<Boolean> readOnlySupported;
|
||||
private final ObservableValue<Boolean> mountFlagsSupported;
|
||||
private final ObservableValue<Path> driveLetter;
|
||||
private final ObservableValue<String> directoryPath;
|
||||
|
||||
|
||||
//-- FXML objects --
|
||||
public CheckBox readOnlyCheckbox;
|
||||
public CheckBox customMountFlagsCheckbox;
|
||||
public TextField mountFlagsField;
|
||||
@@ -49,7 +50,8 @@ public class MountOptionsController implements FxController {
|
||||
public RadioButton mountPointAutoBtn;
|
||||
public RadioButton mountPointDriveLetterBtn;
|
||||
public RadioButton mountPointDirBtn;
|
||||
public ChoiceBox<String> driveLetterSelection;
|
||||
public TextField directoryPathField;
|
||||
public ChoiceBox<Path> driveLetterSelection;
|
||||
|
||||
@Inject
|
||||
MountOptionsController(@VaultOptionsWindow Stage window, @VaultOptionsWindow Vault vault, ObservableValue<MountService> mountService, WindowsDriveLetters windowsDriveLetters, ResourceBundle resourceBundle, Environment environment) {
|
||||
@@ -57,11 +59,12 @@ public class MountOptionsController implements FxController {
|
||||
this.vault = vault;
|
||||
this.windowsDriveLetters = windowsDriveLetters;
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.mountpointDirSupported = mountService.map(s -> s.hasCapability(MountCapability.MOUNT_TO_EXISTING_DIR));
|
||||
this.mountpointDirSupported = mountService.map(s -> s.hasCapability(MountCapability.MOUNT_TO_EXISTING_DIR) || s.hasCapability(MountCapability.MOUNT_WITHIN_EXISTING_PARENT));
|
||||
this.mountpointDriveLetterSupported = mountService.map(s -> s.hasCapability(MountCapability.MOUNT_AS_DRIVE_LETTER));
|
||||
this.mountpointParentSupported = mountService.map(s -> s.hasCapability(MountCapability.MOUNT_WITHIN_EXISTING_PARENT));
|
||||
this.mountFlagsSupported = mountService.map(s -> s.hasCapability(MountCapability.MOUNT_FLAGS));
|
||||
this.readOnlySupported = mountService.map(s -> s.hasCapability(MountCapability.READ_ONLY));
|
||||
this.driveLetter = vault.getVaultSettings().mountPoint().map(p -> isDriveLetter(p) ? p : null);
|
||||
this.directoryPath = vault.getVaultSettings().mountPoint().map(p -> isDriveLetter(p) ? null : p.toString());
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -73,26 +76,24 @@ public class MountOptionsController implements FxController {
|
||||
mountFlagsField.disableProperty().bind(customMountFlagsCheckbox.selectedProperty().not());
|
||||
customMountFlagsCheckbox.setSelected(vault.isHavingCustomMountFlags());
|
||||
|
||||
// mount point options:
|
||||
mountPointToggleGroup.selectedToggleProperty().addListener(this::toggleMountPoint);
|
||||
driveLetterSelection.getItems().addAll(windowsDriveLetters.getAllDriveLetters());
|
||||
//driveLetter choice box
|
||||
driveLetterSelection.getItems().addAll(windowsDriveLetters.getAll());
|
||||
driveLetterSelection.setConverter(new WinDriveLetterLabelConverter(windowsDriveLetters, resourceBundle));
|
||||
driveLetterSelection.setValue(vault.getVaultSettings().winDriveLetter().get());
|
||||
driveLetterSelection.setOnShowing(event -> driveLetterSelection.setConverter(new WinDriveLetterLabelConverter(windowsDriveLetters, resourceBundle))); //TODO: does this work?
|
||||
|
||||
if (vault.getVaultSettings().useCustomMountPath().get() && vault.getVaultSettings().getCustomMountPath().isPresent()) {
|
||||
mountPointToggleGroup.selectToggle(mountPointDirBtn);
|
||||
} else if (!Strings.isNullOrEmpty(vault.getVaultSettings().winDriveLetter().get())) {
|
||||
mountPointToggleGroup.selectToggle(mountPointDriveLetterBtn);
|
||||
} else {
|
||||
//mountPoint toggle group
|
||||
var mountPoint = vault.getVaultSettings().getMountPoint();
|
||||
if (mountPoint == null) {
|
||||
//prepare and select auto
|
||||
mountPointToggleGroup.selectToggle(mountPointAutoBtn);
|
||||
} else if (mountPoint.getParent() == null && isDriveLetter(mountPoint)) {
|
||||
//prepare and select drive letter
|
||||
mountPointToggleGroup.selectToggle(mountPointDriveLetterBtn);
|
||||
} else if (driveLetterSelection.getValue() == null) {
|
||||
//prepare and select dir
|
||||
mountPointToggleGroup.selectToggle(mountPointDirBtn);
|
||||
}
|
||||
|
||||
vault.getVaultSettings().useCustomMountPath().bind(mountPointToggleGroup.selectedToggleProperty().isEqualTo(mountPointDirBtn));
|
||||
vault.getVaultSettings().winDriveLetter().bind( //
|
||||
Bindings.when(mountPointToggleGroup.selectedToggleProperty().isEqualTo(mountPointDriveLetterBtn)) //
|
||||
.then(driveLetterSelection.getSelectionModel().selectedItemProperty()) //
|
||||
.otherwise((String) null) //
|
||||
);
|
||||
mountPointToggleGroup.selectedToggleProperty().addListener(this::selectedToggleChanged);
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -111,16 +112,29 @@ public class MountOptionsController implements FxController {
|
||||
|
||||
@FXML
|
||||
public void chooseCustomMountPoint() {
|
||||
chooseCustomMountPointOrReset(mountPointDirBtn);
|
||||
try {
|
||||
Path chosenPath = chooseCustomMountPointInternal();
|
||||
vault.getVaultSettings().mountPoint().set(chosenPath);
|
||||
} catch (NoDirSelectedException e) {
|
||||
//no-op
|
||||
}
|
||||
}
|
||||
|
||||
private void chooseCustomMountPointOrReset(Toggle previousMountToggle) {
|
||||
/**
|
||||
* Prepares and opens a directory chooser dialog.
|
||||
* This method blocks until the dialog is closed.
|
||||
*
|
||||
* @return the absolute path to the chosen directory
|
||||
* @throws NoDirSelectedException if dialog is closed without choosing a directory
|
||||
*/
|
||||
private Path chooseCustomMountPointInternal() throws NoDirSelectedException {
|
||||
DirectoryChooser directoryChooser = new DirectoryChooser();
|
||||
directoryChooser.setTitle(resourceBundle.getString("vaultOptions.mount.mountPoint.directoryPickerTitle"));
|
||||
try {
|
||||
var initialDir = Path.of(vault.getVaultSettings().getCustomMountPath().orElse(System.getProperty("user.home")));
|
||||
var mp = vault.getVaultSettings().mountPoint().get();
|
||||
var initialDir = mp != null && !isDriveLetter(mp) ? mp : Path.of(System.getProperty("user.home"));
|
||||
|
||||
if (Files.exists(initialDir)) {
|
||||
if (Files.isDirectory(initialDir)) {
|
||||
directoryChooser.setInitialDirectory(initialDir.toFile());
|
||||
}
|
||||
} catch (InvalidPathException e) {
|
||||
@@ -128,49 +142,74 @@ public class MountOptionsController implements FxController {
|
||||
}
|
||||
File file = directoryChooser.showDialog(window);
|
||||
if (file != null) {
|
||||
vault.getVaultSettings().customMountPath().set(file.getAbsolutePath());
|
||||
return file.toPath();
|
||||
} else {
|
||||
mountPointToggleGroup.selectToggle(previousMountToggle);
|
||||
throw new NoDirSelectedException();
|
||||
}
|
||||
}
|
||||
|
||||
private void toggleMountPoint(@SuppressWarnings("unused") ObservableValue<? extends Toggle> observable, Toggle oldValue, Toggle newValue) {
|
||||
if (mountPointDirBtn.equals(newValue) && Strings.isNullOrEmpty(vault.getVaultSettings().customMountPath().get())) {
|
||||
chooseCustomMountPointOrReset(oldValue);
|
||||
private void selectedToggleChanged(ObservableValue<? extends Toggle> observable, Toggle oldToggle, Toggle newToggle) {
|
||||
Path mountPointToBe = null;
|
||||
try {
|
||||
//Remark: the mountpoint corresponding to the newToggle must be null, otherwise it would not be new!
|
||||
if (mountPointDriveLetterBtn.equals(newToggle)) {
|
||||
mountPointToBe = driveLetterSelection.getItems().get(0);
|
||||
} else if (mountPointDirBtn.equals(newToggle)) {
|
||||
mountPointToBe = chooseCustomMountPointInternal();
|
||||
}
|
||||
vault.getVaultSettings().mountPoint().set(mountPointToBe);
|
||||
} catch (NoDirSelectedException e) {
|
||||
if (!mountPointDirBtn.equals(oldToggle)) {
|
||||
mountPointToggleGroup.selectToggle(oldToggle);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts 'C' to "C:" to translate between model and GUI.
|
||||
*/
|
||||
private static class WinDriveLetterLabelConverter extends StringConverter<String> {
|
||||
private boolean isDriveLetter(Path mountPoint) {
|
||||
if (mountPoint != null) {
|
||||
var s = mountPoint.toString();
|
||||
return s.length() == 3 && mountPoint.toString().endsWith(":\\");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private final Set<String> occupiedDriveLetters;
|
||||
private static class WinDriveLetterLabelConverter extends StringConverter<Path> {
|
||||
|
||||
private final Set<Path> occupiedDriveLetters;
|
||||
private final ResourceBundle resourceBundle;
|
||||
|
||||
WinDriveLetterLabelConverter(WindowsDriveLetters windowsDriveLetters, ResourceBundle resourceBundle) {
|
||||
this.occupiedDriveLetters = windowsDriveLetters.getOccupiedDriveLetters();
|
||||
this.occupiedDriveLetters = windowsDriveLetters.getOccupied();
|
||||
this.resourceBundle = resourceBundle;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(String driveLetter) {
|
||||
if (Strings.isNullOrEmpty(driveLetter)) {
|
||||
public String toString(Path driveLetter) {
|
||||
if (driveLetter == null) {
|
||||
return "";
|
||||
} else if (occupiedDriveLetters.contains(driveLetter)) {
|
||||
return driveLetter + ": (" + resourceBundle.getString("vaultOptions.mount.winDriveLetterOccupied") + ")";
|
||||
return driveLetter.toString().substring(0, 2) + " (" + resourceBundle.getString("vaultOptions.mount.winDriveLetterOccupied") + ")";
|
||||
} else {
|
||||
return driveLetter + ":";
|
||||
return driveLetter.toString().substring(0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromString(String string) {
|
||||
throw new UnsupportedOperationException();
|
||||
public Path fromString(String string) {
|
||||
if (string.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return Path.of(string + "\\");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//@formatter:off
|
||||
private static class NoDirSelectedException extends Exception {}
|
||||
//@formatter:on
|
||||
|
||||
// Getter & Setter
|
||||
|
||||
public ObservableValue<Boolean> mountFlagsSupportedProperty() {
|
||||
@@ -189,14 +228,6 @@ public class MountOptionsController implements FxController {
|
||||
return mountpointDirSupported.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<Boolean> mountpointParentSupportedProperty() {
|
||||
return mountpointParentSupported;
|
||||
}
|
||||
|
||||
public boolean isMountpointParentSupported() {
|
||||
return mountpointParentSupported.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<Boolean> mountpointDriveLetterSupportedProperty() {
|
||||
return mountpointDriveLetterSupported;
|
||||
}
|
||||
@@ -213,9 +244,20 @@ public class MountOptionsController implements FxController {
|
||||
return readOnlySupported.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<Path> driveLetterProperty() {
|
||||
return driveLetter;
|
||||
}
|
||||
|
||||
public String getCustomMountPath() {
|
||||
return vault.getVaultSettings().customMountPath().get();
|
||||
public Path getDriveLetter() {
|
||||
return driveLetter.getValue();
|
||||
}
|
||||
|
||||
public ObservableValue<String> directoryPathProperty() {
|
||||
return directoryPath;
|
||||
}
|
||||
|
||||
public String getDirectoryPath() {
|
||||
return directoryPath.getValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user