diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java index dd9808070..df0eaa571 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java @@ -11,7 +11,8 @@ package org.cryptomator.ui.controllers; import com.google.common.base.CharMatcher; import com.google.common.base.Strings; import javafx.application.Application; -import javafx.beans.Observable; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.fxml.FXML; @@ -21,12 +22,11 @@ import javafx.scene.control.Button; import javafx.scene.control.ButtonType; import javafx.scene.control.CheckBox; import javafx.scene.control.ChoiceBox; +import javafx.scene.control.ContentDisplay; import javafx.scene.control.Hyperlink; import javafx.scene.control.Label; -import javafx.scene.control.ProgressIndicator; import javafx.scene.control.TextField; import javafx.scene.input.KeyEvent; -import javafx.scene.layout.GridPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.text.Text; @@ -85,6 +85,7 @@ public class UnlockController implements ViewController { private Vault vault; private Optional listener = Optional.empty(); private Subscription vaultSubs = Subscription.EMPTY; + private BooleanProperty unlocking = new SimpleBooleanProperty(); @Inject public UnlockController(Application app, @Named("mainWindow") Stage mainWindow, Localization localization, WindowsDriveLetters driveLetters, Optional keychainAccess, Settings settings, ExecutorService executor) { @@ -125,7 +126,7 @@ public class UnlockController implements ViewController { private CheckBox revealAfterMount; @FXML - private Label winDriveLetterLabel; + private CheckBox useCustomWinDriveLetter; @FXML private ChoiceBox winDriveLetter; @@ -139,9 +140,6 @@ public class UnlockController implements ViewController { @FXML private Label customMountPointLabel; - @FXML - private ProgressIndicator progressIndicator; - @FXML private Hyperlink downloadsPageLink; @@ -160,7 +158,8 @@ public class UnlockController implements ViewController { @Override public void initialize() { advancedOptions.managedProperty().bind(advancedOptions.visibleProperty()); - unlockButton.disableProperty().bind(passwordField.textProperty().isEmpty()); + advancedOptions.disableProperty().bind(unlocking); + unlockButton.disableProperty().bind(unlocking.or(passwordField.textProperty().isEmpty())); mountName.addEventFilter(KeyEvent.KEY_TYPED, this::filterAlphanumericKeyEvents); mountName.textProperty().addListener(this::mountNameDidChange); useCustomMountFlags.selectedProperty().addListener(this::useCustomMountFlagsDidChange); @@ -168,14 +167,16 @@ public class UnlockController implements ViewController { mountFlags.textProperty().addListener(this::mountFlagsDidChange); savePassword.setDisable(!keychainAccess.isPresent()); unlockAfterStartup.disableProperty().bind(savePassword.disabledProperty().or(savePassword.selectedProperty().not())); + downloadsPageLink.visibleProperty().bind(downloadsPageLink.managedProperty()); customMountPoint.visibleProperty().bind(useCustomMountPoint.selectedProperty()); customMountPoint.managedProperty().bind(useCustomMountPoint.selectedProperty()); winDriveLetter.setConverter(new WinDriveLetterLabelConverter()); + winDriveLetter.disableProperty().bind(useCustomWinDriveLetter.selectedProperty().not()); if (!SystemUtils.IS_OS_WINDOWS) { - winDriveLetterLabel.setVisible(false); - winDriveLetterLabel.setManaged(false); + useCustomWinDriveLetter.setVisible(false); + useCustomWinDriveLetter.setManaged(false); winDriveLetter.setVisible(false); winDriveLetter.setManaged(false); } @@ -205,18 +206,9 @@ public class UnlockController implements ViewController { this.vault = vault; advancedOptions.setVisible(false); advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.show")); - progressIndicator.setVisible(false); + unlockButton.setContentDisplay(ContentDisplay.TEXT_ONLY); state.successMessage().map(localization::getString).ifPresent(messageText::setText); - if (SystemUtils.IS_OS_WINDOWS) { - winDriveLetter.valueProperty().removeListener(driveLetterChangeListener); - winDriveLetter.getItems().clear(); - winDriveLetter.getItems().add(null); - winDriveLetter.getItems().addAll(driveLetters.getAvailableDriveLetters()); - winDriveLetter.getItems().sort(new WinDriveLetterComparator()); - winDriveLetter.valueProperty().addListener(driveLetterChangeListener); - chooseSelectedDriveLetter(); - } - downloadsPageLink.setVisible(false); + downloadsPageLink.setManaged(false); mountName.setText(vault.getMountName()); useCustomMountFlags.setSelected(vault.isHavingCustomMountFlags()); mountFlags.setText(vault.getMountFlags()); @@ -251,10 +243,6 @@ public class UnlockController implements ViewController { // DOKANY-dependent controls: if (VolumeImpl.DOKANY.equals(settings.preferredVolumeImpl().get())) { - winDriveLetter.visibleProperty().bind(useCustomMountPoint.selectedProperty().not()); - winDriveLetter.managedProperty().bind(useCustomMountPoint.selectedProperty().not()); - winDriveLetterLabel.visibleProperty().bind(useCustomMountPoint.selectedProperty().not()); - winDriveLetterLabel.managedProperty().bind(useCustomMountPoint.selectedProperty().not()); // readonly not yet supported by dokany useReadOnlyMode.setSelected(false); useReadOnlyMode.setVisible(false); @@ -265,10 +253,18 @@ public class UnlockController implements ViewController { // OS-dependent controls: if (SystemUtils.IS_OS_WINDOWS) { + winDriveLetter.valueProperty().removeListener(driveLetterChangeListener); + winDriveLetter.getItems().clear(); + winDriveLetter.getItems().add(null); + winDriveLetter.getItems().addAll(driveLetters.getAvailableDriveLetters()); + winDriveLetter.getItems().sort(new WinDriveLetterComparator()); + winDriveLetter.valueProperty().addListener(driveLetterChangeListener); + chooseSelectedDriveLetter(); + winDriveLetter.visibleProperty().bind(useCustomMountPoint.selectedProperty().not()); winDriveLetter.managedProperty().bind(useCustomMountPoint.selectedProperty().not()); - winDriveLetterLabel.visibleProperty().bind(useCustomMountPoint.selectedProperty().not()); - winDriveLetterLabel.managedProperty().bind(useCustomMountPoint.selectedProperty().not()); + useCustomWinDriveLetter.visibleProperty().bind(useCustomMountPoint.selectedProperty().not()); + useCustomWinDriveLetter.managedProperty().bind(useCustomMountPoint.selectedProperty().not()); } vaultSubs = vaultSubs.and(EasyBind.subscribe(unlockAfterStartup.selectedProperty(), vaultSettings.unlockAfterStartup()::set)); @@ -305,6 +301,7 @@ public class UnlockController implements ViewController { @FXML private void didClickAdvancedOptionsButton() { messageText.setText(null); + downloadsPageLink.setManaged(false); advancedOptions.setVisible(!advancedOptions.isVisible()); if (advancedOptions.isVisible()) { advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.hide")); @@ -355,6 +352,13 @@ public class UnlockController implements ViewController { } } + @FXML + public void didClickCustomWinDriveLetterCheckbox() { + if (!useCustomWinDriveLetter.isSelected()) { + winDriveLetter.setValue(null); + } + } + /** * Converts 'C' to "C:" to translate between model and GUI. */ @@ -404,7 +408,7 @@ public class UnlockController implements ViewController { private void chooseSelectedDriveLetter() { assert SystemUtils.IS_OS_WINDOWS; // if the vault prefers a drive letter, that is currently occupied, this is our last chance to reset this: - if (driveLetters.getOccupiedDriveLetters().contains(vault.getWinDriveLetter())) { + if (vault.getWinDriveLetter() != null && driveLetters.getOccupiedDriveLetters().contains(vault.getWinDriveLetter())) { vault.setWinDriveLetter(null); } final Character letter = vault.getWinDriveLetter(); @@ -453,10 +457,9 @@ public class UnlockController implements ViewController { @FXML private void didClickUnlockButton() { - advancedOptions.setDisable(true); - advancedOptions.setVisible(false); + unlocking.set(true); advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.show")); - progressIndicator.setVisible(true); + unlockButton.setContentDisplay(ContentDisplay.LEFT); CharSequence password = passwordField.getCharacters(); Tasks.create(() -> { @@ -465,22 +468,23 @@ public class UnlockController implements ViewController { keychainAccess.get().storePassphrase(vault.getId(), password); } }).onSuccess(() -> { - messageText.setText(""); - downloadsPageLink.setVisible(false); + messageText.setText(null); + downloadsPageLink.setManaged(false); listener.ifPresent(lstnr -> lstnr.didUnlock(vault)); passwordField.swipe(); }).onError(InvalidPassphraseException.class, e -> { messageText.setText(localization.getString("unlock.errorMessage.wrongPassword")); + downloadsPageLink.setManaged(false); passwordField.selectAll(); passwordField.requestFocus(); }).onError(UnsupportedVaultFormatException.class, e -> { if (e.isVaultOlderThanSoftware()) { // whitespace after localized text used as separator between text and hyperlink messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " "); - downloadsPageLink.setVisible(true); + downloadsPageLink.setManaged(true); } else if (e.isSoftwareOlderThanVault()) { messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " "); - downloadsPageLink.setVisible(true); + downloadsPageLink.setManaged(true); } else if (e.getDetectedVersion() == Integer.MAX_VALUE) { messageText.setText(localization.getString("unlock.errorMessage.unauthenticVersionMac")); } @@ -488,18 +492,21 @@ public class UnlockController implements ViewController { LOG.error("Unlock failed. Mount point not a directory: {}", e.getMessage()); advancedOptions.setVisible(true); messageText.setText(null); + downloadsPageLink.setManaged(false); showUnlockFailedErrorDialog("unlock.failedDialog.content.mountPathNonExisting"); }).onError(DirectoryNotEmptyException.class, e -> { LOG.error("Unlock failed. Mount point not empty: {}", e.getMessage()); advancedOptions.setVisible(true); messageText.setText(null); + downloadsPageLink.setManaged(false); showUnlockFailedErrorDialog("unlock.failedDialog.content.mountPathNotEmpty"); }).onError(Exception.class, e -> { // including RuntimeExceptions LOG.error("Unlock failed for technical reasons.", e); messageText.setText(localization.getString("unlock.errorMessage.unlockFailed")); + downloadsPageLink.setManaged(false); }).andFinally(() -> { - advancedOptions.setDisable(false); - progressIndicator.setVisible(false); + unlocking.set(false); + unlockButton.setContentDisplay(ContentDisplay.TEXT_ONLY); }).runOnce(executor); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java b/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java index 05efef31d..c42696c01 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java +++ b/main/ui/src/main/java/org/cryptomator/ui/model/WindowsDriveLetters.java @@ -8,6 +8,8 @@ package org.cryptomator.ui.model; import org.apache.commons.lang3.CharUtils; import org.apache.commons.lang3.SystemUtils; import org.cryptomator.common.FxApplicationScoped; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import javax.inject.Inject; import java.nio.file.FileSystems; @@ -21,6 +23,7 @@ import java.util.stream.StreamSupport; @FxApplicationScoped public final class WindowsDriveLetters { + private static final Logger LOG = LoggerFactory.getLogger(WindowsDriveLetters.class); private static final Set D_TO_Z; static { @@ -35,10 +38,12 @@ public final class WindowsDriveLetters { public Set getOccupiedDriveLetters() { if (!SystemUtils.IS_OS_WINDOWS) { - throw new UnsupportedOperationException("This method is only defined for Windows file systems"); + LOG.warn("Attempted to get occupied drive letters on non-Windows machine."); + return Set.of(); + } else { + Iterable rootDirs = FileSystems.getDefault().getRootDirectories(); + return StreamSupport.stream(rootDirs.spliterator(), false).map(Path::toString).map(CharUtils::toChar).map(Character::toUpperCase).collect(Collectors.toSet()); } - Iterable rootDirs = FileSystems.getDefault().getRootDirectories(); - return StreamSupport.stream(rootDirs.spliterator(), false).map(Path::toString).map(CharUtils::toChar).map(Character::toUpperCase).collect(Collectors.toSet()); } public Set getAvailableDriveLetters() { diff --git a/main/ui/src/main/resources/fxml/unlock.fxml b/main/ui/src/main/resources/fxml/unlock.fxml index 29a9b82e3..0dfc523e9 100644 --- a/main/ui/src/main/resources/fxml/unlock.fxml +++ b/main/ui/src/main/resources/fxml/unlock.fxml @@ -25,7 +25,7 @@ - + @@ -35,14 +35,25 @@ - + + + + + + + + + - + @@ -81,17 +92,10 @@ - - - - - - - - -