mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-17 18:21:26 +00:00
Re-layouted unlock UI and added textfield for custom mount flags (atm only supported for FUSE - see #802)
This commit is contained in:
@@ -11,6 +11,7 @@ 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.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.fxml.FXML;
|
||||
@@ -27,6 +28,7 @@ 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;
|
||||
import javafx.stage.DirectoryChooser;
|
||||
import javafx.stage.Stage;
|
||||
@@ -113,6 +115,12 @@ public class UnlockController implements ViewController {
|
||||
@FXML
|
||||
private TextField mountName;
|
||||
|
||||
@FXML
|
||||
private CheckBox useCustomMountFlags;
|
||||
|
||||
@FXML
|
||||
private TextField mountFlags;
|
||||
|
||||
@FXML
|
||||
private CheckBox revealAfterMount;
|
||||
|
||||
@@ -134,17 +142,14 @@ public class UnlockController implements ViewController {
|
||||
@FXML
|
||||
private ProgressIndicator progressIndicator;
|
||||
|
||||
@FXML
|
||||
private Text progressText;
|
||||
|
||||
@FXML
|
||||
private Hyperlink downloadsPageLink;
|
||||
|
||||
@FXML
|
||||
private GridPane advancedOptions;
|
||||
private VBox advancedOptions;
|
||||
|
||||
@FXML
|
||||
private GridPane root;
|
||||
private VBox root;
|
||||
|
||||
@FXML
|
||||
private CheckBox unlockAfterStartup;
|
||||
@@ -158,6 +163,9 @@ public class UnlockController implements ViewController {
|
||||
unlockButton.disableProperty().bind(passwordField.textProperty().isEmpty());
|
||||
mountName.addEventFilter(KeyEvent.KEY_TYPED, this::filterAlphanumericKeyEvents);
|
||||
mountName.textProperty().addListener(this::mountNameDidChange);
|
||||
useCustomMountFlags.selectedProperty().addListener(this::useCustomMountFlagsDidChange);
|
||||
mountFlags.disableProperty().bind(useCustomMountFlags.selectedProperty().not());
|
||||
mountFlags.textProperty().addListener(this::mountFlagsDidChange);
|
||||
savePassword.setDisable(!keychainAccess.isPresent());
|
||||
unlockAfterStartup.disableProperty().bind(savePassword.disabledProperty().or(savePassword.selectedProperty().not()));
|
||||
|
||||
@@ -173,7 +181,6 @@ public class UnlockController implements ViewController {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Parent getRoot() {
|
||||
return root;
|
||||
@@ -199,7 +206,6 @@ public class UnlockController implements ViewController {
|
||||
advancedOptions.setVisible(false);
|
||||
advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.show"));
|
||||
progressIndicator.setVisible(false);
|
||||
progressText.setText(null);
|
||||
state.successMessage().map(localization::getString).ifPresent(messageText::setText);
|
||||
if (SystemUtils.IS_OS_WINDOWS) {
|
||||
winDriveLetter.valueProperty().removeListener(driveLetterChangeListener);
|
||||
@@ -212,6 +218,8 @@ public class UnlockController implements ViewController {
|
||||
}
|
||||
downloadsPageLink.setVisible(false);
|
||||
mountName.setText(vault.getMountName());
|
||||
useCustomMountFlags.setSelected(vault.isHavingCustomMountFlags());
|
||||
mountFlags.setText(vault.getMountFlags());
|
||||
savePassword.setSelected(false);
|
||||
// auto-fill pw from keychain:
|
||||
if (keychainAccess.isPresent()) {
|
||||
@@ -318,6 +326,23 @@ public class UnlockController implements ViewController {
|
||||
} else {
|
||||
vault.setMountName(newValue);
|
||||
}
|
||||
if (!useCustomMountFlags.isSelected()) {
|
||||
mountFlags.setText(vault.getMountFlags()); // flags might depend on the volume name
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void useCustomMountFlagsDidChange(@SuppressWarnings("unused") ObservableValue<? extends Boolean> property, @SuppressWarnings("unused")Boolean oldValue, Boolean newValue) {
|
||||
if (!newValue) {
|
||||
vault.setMountFlags(VaultSettings.DEFAULT_MOUNT_FLAGS);
|
||||
mountFlags.setText(vault.getMountFlags());
|
||||
}
|
||||
}
|
||||
|
||||
private void mountFlagsDidChange(@SuppressWarnings("unused") ObservableValue<? extends String> property, @SuppressWarnings("unused")String oldValue, String newValue) {
|
||||
if (useCustomMountFlags.isSelected()) {
|
||||
vault.setMountFlags(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
@FXML
|
||||
@@ -435,7 +460,6 @@ public class UnlockController implements ViewController {
|
||||
|
||||
CharSequence password = passwordField.getCharacters();
|
||||
Tasks.create(() -> {
|
||||
progressText.setText(localization.getString("unlock.pendingMessage.unlocking"));
|
||||
vault.unlock(password);
|
||||
if (keychainAccess.isPresent() && savePassword.isSelected()) {
|
||||
keychainAccess.get().storePassphrase(vault.getId(), password);
|
||||
@@ -476,7 +500,6 @@ public class UnlockController implements ViewController {
|
||||
}).andFinally(() -> {
|
||||
advancedOptions.setDisable(false);
|
||||
progressIndicator.setVisible(false);
|
||||
progressText.setText(null);
|
||||
}).runOnce(executor);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import javax.inject.Qualifier;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
@Qualifier
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
public @interface DefaultMountFlags {
|
||||
}
|
||||
@@ -44,7 +44,7 @@ public class DokanyVolume implements Volume {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mount(CryptoFileSystem fs) throws VolumeException, IOException {
|
||||
public void mount(CryptoFileSystem fs, String mountFlags) throws VolumeException, IOException {
|
||||
Path mountPath = getMountPoint();
|
||||
String mountName = vaultSettings.mountName().get();
|
||||
try {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.Environment;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
@@ -21,7 +22,6 @@ import java.nio.file.Files;
|
||||
import java.nio.file.NotDirectoryException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class FuseVolume implements Volume {
|
||||
@@ -44,7 +44,7 @@ public class FuseVolume implements Volume {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mount(CryptoFileSystem fs) throws IOException, FuseNotSupportedException, VolumeException {
|
||||
public void mount(CryptoFileSystem fs, String mountFlags) throws IOException, FuseNotSupportedException, VolumeException {
|
||||
Optional<String> optionalCustomMountPoint = vaultSettings.getIndividualMountPath();
|
||||
if (optionalCustomMountPoint.isPresent()) {
|
||||
Path customMountPoint = Paths.get(optionalCustomMountPoint.get());
|
||||
@@ -55,7 +55,7 @@ public class FuseVolume implements Volume {
|
||||
this.mountPoint = prepareTemporaryMountPoint();
|
||||
LOG.debug("Successfully created mount point: {}", mountPoint);
|
||||
}
|
||||
mount(fs.getPath("/"));
|
||||
mount(fs.getPath("/"), mountFlags);
|
||||
}
|
||||
|
||||
private void checkProvidedMountPoint(Path mountPoint) throws IOException {
|
||||
@@ -96,11 +96,11 @@ public class FuseVolume implements Volume {
|
||||
throw new VolumeException("Did not find feasible mount point.");
|
||||
}
|
||||
|
||||
private void mount(Path root) throws VolumeException {
|
||||
private void mount(Path root, String mountFlags) throws VolumeException {
|
||||
try {
|
||||
Mounter mounter = FuseMountFactory.getMounter();
|
||||
EnvironmentVariables envVars = EnvironmentVariables.create() //
|
||||
.withFlags(mountFlags(mounter))
|
||||
.withFlags(splitFlags(mountFlags))
|
||||
.withMountPoint(mountPoint) //
|
||||
.build();
|
||||
this.fuseMnt = mounter.mount(root, envVars);
|
||||
@@ -109,13 +109,8 @@ public class FuseVolume implements Volume {
|
||||
}
|
||||
}
|
||||
|
||||
private String[] mountFlags(Mounter mounter) {
|
||||
List<String> mountFlags = vaultSettings.mountFlags().get();
|
||||
if (mountFlags.isEmpty()) {
|
||||
return mounter.defaultMountFlags();
|
||||
} else {
|
||||
return mountFlags.toArray(String[]::new);
|
||||
}
|
||||
private String[] splitFlags(String str) {
|
||||
return Splitter.on(' ').splitToList(str).toArray(String[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
13
main/ui/src/main/java/org/cryptomator/ui/model/PerVault.java
Normal file
13
main/ui/src/main/java/org/cryptomator/ui/model/PerVault.java
Normal file
@@ -0,0 +1,13 @@
|
||||
package org.cryptomator.ui.model;
|
||||
|
||||
import javax.inject.Scope;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Scope
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface PerVault {
|
||||
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import org.cryptomator.cryptofs.CryptoFileSystemProperties.FileSystemFlags;
|
||||
import org.cryptomator.cryptofs.CryptoFileSystemProvider;
|
||||
import org.cryptomator.cryptolib.api.CryptoException;
|
||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||
import org.cryptomator.ui.model.VaultModule.PerVault;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -44,6 +43,7 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@PerVault
|
||||
public class Vault {
|
||||
@@ -54,6 +54,7 @@ public class Vault {
|
||||
|
||||
private final VaultSettings vaultSettings;
|
||||
private final Provider<Volume> volumeProvider;
|
||||
private final Supplier<String> defaultMountFlags;
|
||||
private final AtomicReference<CryptoFileSystem> cryptoFileSystem = new AtomicReference<>();
|
||||
private final ObjectProperty<State> state = new SimpleObjectProperty<State>(State.LOCKED);
|
||||
|
||||
@@ -64,9 +65,10 @@ public class Vault {
|
||||
}
|
||||
|
||||
@Inject
|
||||
Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider) {
|
||||
Vault(VaultSettings vaultSettings, Provider<Volume> volumeProvider, @DefaultMountFlags Supplier<String> defaultMountFlags) {
|
||||
this.vaultSettings = vaultSettings;
|
||||
this.volumeProvider = volumeProvider;
|
||||
this.defaultMountFlags = defaultMountFlags;
|
||||
}
|
||||
|
||||
// ******************************************************************************
|
||||
@@ -110,7 +112,7 @@ public class Vault {
|
||||
}
|
||||
CryptoFileSystem fs = getCryptoFileSystem(passphrase);
|
||||
volume = volumeProvider.get();
|
||||
volume.mount(fs);
|
||||
volume.mount(fs, getMountFlags());
|
||||
Platform.runLater(() -> {
|
||||
state.set(State.UNLOCKED);
|
||||
});
|
||||
@@ -241,10 +243,6 @@ public class Vault {
|
||||
}
|
||||
}
|
||||
|
||||
public String getMountName() {
|
||||
return vaultSettings.mountName().get();
|
||||
}
|
||||
|
||||
public String getCustomMountPath() {
|
||||
return vaultSettings.individualMountPath().getValueSafe();
|
||||
}
|
||||
@@ -253,6 +251,10 @@ public class Vault {
|
||||
vaultSettings.individualMountPath().set(mountPath);
|
||||
}
|
||||
|
||||
public String getMountName() {
|
||||
return vaultSettings.mountName().get();
|
||||
}
|
||||
|
||||
public void setMountName(String mountName) throws IllegalArgumentException {
|
||||
if (StringUtils.isBlank(mountName)) {
|
||||
throw new IllegalArgumentException("mount name is empty");
|
||||
@@ -261,6 +263,23 @@ public class Vault {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isHavingCustomMountFlags() {
|
||||
return !Strings.isNullOrEmpty(vaultSettings.mountFlags().get());
|
||||
}
|
||||
|
||||
public String getMountFlags() {
|
||||
String mountFlags = vaultSettings.mountFlags().get();
|
||||
if (Strings.isNullOrEmpty(mountFlags)) {
|
||||
return defaultMountFlags.get();
|
||||
} else {
|
||||
return mountFlags;
|
||||
}
|
||||
}
|
||||
|
||||
public void setMountFlags(String mountFlags) {
|
||||
vaultSettings.mountFlags().set(mountFlags);
|
||||
}
|
||||
|
||||
public Character getWinDriveLetter() {
|
||||
if (vaultSettings.winDriveLetter().get() == null) {
|
||||
return null;
|
||||
|
||||
@@ -7,7 +7,6 @@ package org.cryptomator.ui.model;
|
||||
|
||||
import dagger.BindsInstance;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.ui.model.VaultModule.PerVault;
|
||||
|
||||
import dagger.Subcomponent;
|
||||
|
||||
|
||||
@@ -7,28 +7,24 @@ package org.cryptomator.ui.model;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.common.settings.Settings;
|
||||
import org.cryptomator.common.settings.VaultSettings;
|
||||
import org.cryptomator.common.settings.VolumeImpl;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.inject.Scope;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Module
|
||||
public class VaultModule {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(VaultModule.class);
|
||||
|
||||
@Scope
|
||||
@Documented
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface PerVault {
|
||||
|
||||
}
|
||||
|
||||
@Provides
|
||||
public Volume provideVolume(Settings settings, WebDavVolume webDavVolume, FuseVolume fuseVolume, DokanyVolume dokanyVolume) {
|
||||
VolumeImpl preferredImpl = settings.preferredVolumeImpl().get();
|
||||
@@ -45,4 +41,65 @@ public class VaultModule {
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
@PerVault
|
||||
@DefaultMountFlags
|
||||
public Supplier<String> provideDefaultMountFlags(Settings settings, VaultSettings vaultSettings) {
|
||||
VolumeImpl preferredImpl = settings.preferredVolumeImpl().get();
|
||||
switch (preferredImpl) {
|
||||
case FUSE:
|
||||
if (SystemUtils.IS_OS_MAC_OSX) {
|
||||
return () -> getMacFuseDefaultMountFlags(settings, vaultSettings);
|
||||
} else if (SystemUtils.IS_OS_LINUX) {
|
||||
return () -> getLinuxFuseDefaultMountFlags(settings, vaultSettings);
|
||||
}
|
||||
case DOKANY:
|
||||
return () -> getDokanyDefaultMountFlags(settings, vaultSettings);
|
||||
default:
|
||||
return () -> "--flags-supported-on-FUSE-or-DOKANY-only";
|
||||
}
|
||||
}
|
||||
|
||||
private String getMacFuseDefaultMountFlags(Settings settings, VaultSettings vaultSettings) {
|
||||
assert SystemUtils.IS_OS_MAC_OSX;
|
||||
// see: https://github.com/osxfuse/osxfuse/wiki/Mount-options
|
||||
try {
|
||||
Path userHome = Paths.get(System.getProperty("user.home"));
|
||||
int uid = (int) Files.getAttribute(userHome, "unix:uid");
|
||||
int gid = (int) Files.getAttribute(userHome, "unix:gid");
|
||||
return "-ovolname=" + vaultSettings.mountName().get() // volume name
|
||||
+ " -ouid=" + uid //
|
||||
+ " -ogid=" + gid //
|
||||
+ " -oatomic_o_trunc" //
|
||||
+ " -oauto_xattr" //
|
||||
+ " -oauto_cache" //
|
||||
+ " -omodules=iconv,from_code=UTF-8,to_code=UTF-8-MAC" // show files names in Unicode NFD encoding
|
||||
+ " -onoappledouble" // vastly impacts performance for some reason...
|
||||
+ " -odefault_permissions"; // let the kernel assume permissions based on file attributes etc
|
||||
} catch (IOException e) {
|
||||
LOG.error("Could not read uid/gid from USER_HOME", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private String getLinuxFuseDefaultMountFlags(Settings settings, VaultSettings vaultSettings) {
|
||||
assert SystemUtils.IS_OS_LINUX;
|
||||
try {
|
||||
Path userHome = Paths.get(System.getProperty("user.home"));
|
||||
int uid = (int) Files.getAttribute(userHome, "unix:uid");
|
||||
int gid = (int) Files.getAttribute(userHome, "unix:gid");
|
||||
return "-oauto_unmount" //
|
||||
+ " -ouid=" + uid //
|
||||
+ " -ogid=" + gid;
|
||||
} catch (IOException e) {
|
||||
LOG.error("Could not read uid/gid from USER_HOME", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private String getDokanyDefaultMountFlags(Settings settings, VaultSettings vaultSettings) {
|
||||
// TODO
|
||||
return "--not-yet-supported";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ public interface Volume {
|
||||
* @param fs
|
||||
* @throws IOException
|
||||
*/
|
||||
void mount(CryptoFileSystem fs) throws IOException, VolumeException;
|
||||
void mount(CryptoFileSystem fs, String mountFlags) throws IOException, VolumeException;
|
||||
|
||||
void reveal() throws VolumeException;
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import org.cryptomator.frontend.webdav.servlet.WebDavServletController;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
@@ -35,7 +34,7 @@ public class WebDavVolume implements Volume {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mount(CryptoFileSystem fs) throws VolumeException {
|
||||
public void mount(CryptoFileSystem fs, String mountFlags) throws VolumeException {
|
||||
if (server == null) {
|
||||
server = serverProvider.get();
|
||||
}
|
||||
|
||||
@@ -17,102 +17,81 @@
|
||||
<?import javafx.scene.control.ProgressIndicator?>
|
||||
<?import javafx.scene.control.Separator?>
|
||||
<?import javafx.scene.control.TextField?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.text.Text?>
|
||||
<?import javafx.scene.text.TextFlow?>
|
||||
<?import org.cryptomator.ui.controls.SecPasswordField?>
|
||||
<GridPane fx:controller="org.cryptomator.ui.controllers.UnlockController" fx:id="root" vgap="12.0" hgap="12.0" prefWidth="400.0" xmlns:fx="http://javafx.com/fxml" cacheShape="true" cache="true">
|
||||
<VBox fx:controller="org.cryptomator.ui.controllers.UnlockController" fx:id="root" spacing="12" alignment="BOTTOM_CENTER" xmlns:fx="http://javafx.com/fxml" prefWidth="400">
|
||||
|
||||
<padding>
|
||||
<Insets top="24.0" right="12.0" bottom="24.0" left="12.0" />
|
||||
<Insets top="24"/>
|
||||
</padding>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints percentWidth="38.2"/>
|
||||
<ColumnConstraints percentWidth="61.8"/>
|
||||
</columnConstraints>
|
||||
<!-- Password Field -->
|
||||
<HBox spacing="12" alignment="BASELINE_LEFT">
|
||||
<Label text="%unlock.label.password" HBox.hgrow="NEVER"/>
|
||||
<SecPasswordField fx:id="passwordField" capslockWarning="%ctrl.secPasswordField.capsLocked" nonPrintableCharsWarning="%ctrl.secPasswordField.nonPrintableChars" maxWidth="Infinity" HBox.hgrow="ALWAYS"/>
|
||||
</HBox>
|
||||
|
||||
<children>
|
||||
<!-- Row 0 -->
|
||||
<Label text="%unlock.label.password" GridPane.rowIndex="0" GridPane.columnIndex="0" cacheShape="true" cache="true" />
|
||||
<SecPasswordField fx:id="passwordField" capslockWarning="%ctrl.secPasswordField.capsLocked" nonPrintableCharsWarning="%ctrl.secPasswordField.nonPrintableChars" GridPane.rowIndex="0" GridPane.columnIndex="1" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
|
||||
<!-- Unlock Button / Advanced Options Button -->
|
||||
<HBox spacing="12.0" alignment="CENTER_RIGHT">
|
||||
<Button fx:id="advancedOptionsButton" text="%unlock.button.advancedOptions.show" prefWidth="150.0" onAction="#didClickAdvancedOptionsButton"/>
|
||||
<Button fx:id="unlockButton" text="%unlock.button.unlock" defaultButton="true" prefWidth="150.0" onAction="#didClickUnlockButton" disable="true"/>
|
||||
<ProgressIndicator progress="-1" fx:id="progressIndicator"/>
|
||||
</HBox>
|
||||
|
||||
<!-- Row 1 -->
|
||||
<HBox GridPane.rowIndex="2" GridPane.columnIndex="0" GridPane.columnSpan="2" spacing="12.0" alignment="CENTER_RIGHT" cacheShape="true" cache="true">
|
||||
<Button fx:id="advancedOptionsButton" text="%unlock.button.advancedOptions.show" prefWidth="150.0" onAction="#didClickAdvancedOptionsButton" cacheShape="true" cache="true" />
|
||||
<Button fx:id="unlockButton" text="%unlock.button.unlock" defaultButton="true" prefWidth="150.0" onAction="#didClickUnlockButton" disable="true" cacheShape="true" cache="true" />
|
||||
<!-- Advanced Options -->
|
||||
<VBox fx:id="advancedOptions" spacing="6" VBox.vgrow="ALWAYS" visible="false">
|
||||
|
||||
<Separator/>
|
||||
|
||||
<!-- Mount Name -->
|
||||
<HBox spacing="12" alignment="BASELINE_LEFT">
|
||||
<Label text="%unlock.label.mountName"/>
|
||||
<TextField fx:id="mountName" HBox.hgrow="ALWAYS" maxWidth="Infinity"/>
|
||||
</HBox>
|
||||
|
||||
<!-- Row 3 -->
|
||||
<Text fx:id="messageText" cache="true" visible="true" GridPane.rowIndex="3" GridPane.columnIndex="0" GridPane.columnSpan="2"/>
|
||||
<!-- Save Password -->
|
||||
<CheckBox fx:id="savePassword" text="%unlock.label.savePassword" onAction="#didClickSavePasswordCheckbox"/>
|
||||
|
||||
<!-- Row 3 -->
|
||||
<GridPane fx:id="advancedOptions" vgap="12.0" hgap="12.0" prefWidth="400.0" GridPane.rowIndex="3" GridPane.columnIndex="0" GridPane.columnSpan="2" visible="false" cacheShape="true" cache="true">
|
||||
<!-- Auto Unlock -->
|
||||
<CheckBox fx:id="unlockAfterStartup" text="%unlock.label.unlockAfterStartup"/>
|
||||
|
||||
<!-- Reveal Drive -->
|
||||
<CheckBox fx:id="revealAfterMount" text="%unlock.label.revealAfterMount"/>
|
||||
|
||||
<!-- Read-Only -->
|
||||
<CheckBox fx:id="useReadOnlyMode" text="%unlock.label.useReadOnlyMode"/>
|
||||
|
||||
<!-- Custom Mount Point -->
|
||||
<CheckBox fx:id="useCustomMountPoint" text="%unlock.label.useOwnMountPath"/>
|
||||
<HBox fx:id="customMountPoint" spacing="6" alignment="BASELINE_LEFT">
|
||||
<padding>
|
||||
<Insets top="24.0" />
|
||||
<Insets left="20.0"/>
|
||||
</padding>
|
||||
<Label HBox.hgrow="ALWAYS" fx:id="customMountPointLabel" textOverrun="LEADING_ELLIPSIS"/>
|
||||
<Button HBox.hgrow="NEVER" minWidth="-Infinity" text="" styleClass="ionicons" onAction="#didClickChooseCustomMountPoint" focusTraversable="true"/>
|
||||
</HBox>
|
||||
|
||||
<columnConstraints>
|
||||
<ColumnConstraints percentWidth="38.2"/>
|
||||
<ColumnConstraints percentWidth="61.8"/>
|
||||
</columnConstraints>
|
||||
<!-- Mount Flags -->
|
||||
<HBox spacing="12" alignment="BASELINE_LEFT">
|
||||
<CheckBox fx:id="useCustomMountFlags" text="%unlock.label.useCustomMountFlags"/>
|
||||
<TextField fx:id="mountFlags" HBox.hgrow="ALWAYS" maxWidth="Infinity"/>
|
||||
</HBox>
|
||||
|
||||
<!-- Row 3.0 -->
|
||||
<Separator GridPane.rowIndex="0" GridPane.columnIndex="0" GridPane.columnSpan="2" cacheShape="true" cache="true"/>
|
||||
<HBox alignment="CENTER" prefWidth="400.0" GridPane.rowIndex="0" GridPane.columnIndex="0" GridPane.columnSpan="2" cacheShape="true" cache="true">
|
||||
<Label text="%unlock.label.advancedHeading" style="-fx-background-color: COLOR_BACKGROUND;" cacheShape="true" cache="true">
|
||||
<padding>
|
||||
<Insets left="6.0" right="6.0"/>
|
||||
</padding>
|
||||
</Label>
|
||||
</HBox>
|
||||
<!-- Windows Drive Letter -->
|
||||
<HBox spacing="12">
|
||||
<Label fx:id="winDriveLetterLabel" text="%unlock.label.winDriveLetter"/>
|
||||
<ChoiceBox fx:id="winDriveLetter" maxWidth="Infinity"/>
|
||||
</HBox>
|
||||
</VBox>
|
||||
|
||||
<!-- Row 3.1 -->
|
||||
<CheckBox GridPane.rowIndex="1" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="savePassword" text="%unlock.label.savePassword" onAction="#didClickSavePasswordCheckbox" cacheShape="true" cache="true" />
|
||||
|
||||
<!-- Row 3.2 -->
|
||||
<CheckBox GridPane.rowIndex="2" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="unlockAfterStartup" text="%unlock.label.unlockAfterStartup" cacheShape="true" cache="true" />
|
||||
|
||||
<!-- Row 3.3 -->
|
||||
<Label GridPane.rowIndex="3" GridPane.columnIndex="0" text="%unlock.label.mountName" cacheShape="true" cache="true" />
|
||||
<TextField GridPane.rowIndex="3" GridPane.columnIndex="1" fx:id="mountName" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
|
||||
|
||||
<!-- Row 3.4 -->
|
||||
<CheckBox GridPane.rowIndex="4" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="revealAfterMount" text="%unlock.label.revealAfterMount" cacheShape="true" cache="true" />
|
||||
|
||||
<!-- Row 3.5 -->
|
||||
<CheckBox GridPane.rowIndex="5" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="useReadOnlyMode" text="%unlock.label.useReadOnlyMode" cacheShape="true" cache="true" />
|
||||
|
||||
<!-- Row 3.6 -->
|
||||
<CheckBox GridPane.rowIndex="6" GridPane.columnIndex="0" GridPane.columnSpan="2" fx:id="useCustomMountPoint" text="%unlock.label.useOwnMountPath" cacheShape="true" cache="true" />
|
||||
|
||||
<!-- Row 3.7 Alt1 -->
|
||||
<Label GridPane.rowIndex="7" GridPane.columnIndex="0" fx:id="winDriveLetterLabel" text="%unlock.label.winDriveLetter" cacheShape="true" cache="true" />
|
||||
<ChoiceBox GridPane.rowIndex="7" GridPane.columnIndex="1" fx:id="winDriveLetter" GridPane.hgrow="ALWAYS" maxWidth="Infinity" cacheShape="true" cache="true" />
|
||||
|
||||
<!-- Row 3.7 Alt2 -->
|
||||
<HBox fx:id="customMountPoint" GridPane.rowIndex="7" GridPane.columnIndex="0" GridPane.columnSpan="2" spacing="6" alignment="BASELINE_LEFT" cacheShape="true" cache="true">
|
||||
<padding>
|
||||
<Insets left="20.0" />
|
||||
</padding>
|
||||
<Label HBox.hgrow="ALWAYS" fx:id="customMountPointLabel" textOverrun="LEADING_ELLIPSIS" cacheShape="true" cache="true" />
|
||||
<Button HBox.hgrow="NEVER" minWidth="-Infinity" text="" styleClass="ionicons" onAction="#didClickChooseCustomMountPoint" focusTraversable="true" cacheShape="true" cache="true" />
|
||||
</HBox>
|
||||
</GridPane>
|
||||
|
||||
<!-- Row 4 -->
|
||||
<TextFlow GridPane.rowIndex="4" GridPane.columnIndex="0" GridPane.columnSpan="2" cacheShape="true" cache="true">
|
||||
<GridPane.margin>
|
||||
<Insets top="24.0"/>
|
||||
</GridPane.margin>
|
||||
<children>
|
||||
<Hyperlink fx:id="downloadsPageLink" text="%unlock.label.downloadsPageLink" visible="false" onAction="#didClickDownloadsLink" cacheShape="true" cache="true" />
|
||||
</children>
|
||||
</TextFlow>
|
||||
|
||||
<!-- Row 5 -->
|
||||
<VBox GridPane.rowIndex="5" GridPane.columnIndex="0" GridPane.columnSpan="2" spacing="12.0" alignment="CENTER" cacheShape="true" cache="true">
|
||||
<ProgressIndicator progress="-1" fx:id="progressIndicator" cacheShape="true" cache="true" cacheHint="SPEED" />
|
||||
<Text fx:id="progressText" cache="true" />
|
||||
</VBox>
|
||||
</children>
|
||||
</GridPane>
|
||||
<!-- Status Text -->
|
||||
<TextFlow>
|
||||
<children>
|
||||
<Text fx:id="messageText" visible="true"/>
|
||||
<Hyperlink fx:id="downloadsPageLink" text="%unlock.label.downloadsPageLink" visible="false" onAction="#didClickDownloadsLink"/>
|
||||
</children>
|
||||
</TextFlow>
|
||||
</VBox>
|
||||
|
||||
@@ -74,14 +74,14 @@ upgrade.version5toX.msg=This vault needs to be migrated to a newer format.\nPlea
|
||||
unlock.label.password=Password
|
||||
unlock.label.savePassword=Save Password
|
||||
unlock.label.mountName=Drive Name
|
||||
unlock.label.useCustomMountFlags=Custom Mount Flags
|
||||
unlock.label.unlockAfterStartup=Auto-Unlock on Start (Experimental)
|
||||
unlock.label.revealAfterMount=Reveal Drive
|
||||
unlock.label.useReadOnlyMode=Read-Only
|
||||
unlock.label.winDriveLetter=Drive Letter
|
||||
unlock.label.useOwnMountPath=Use Custom Mount Point
|
||||
unlock.label.useOwnMountPath=Custom Mount Point
|
||||
unlock.label.chooseMountPath=Choose empty directory…
|
||||
unlock.label.downloadsPageLink=All Cryptomator versions
|
||||
unlock.label.advancedHeading=Advanced Options
|
||||
unlock.button.unlock=Unlock Vault
|
||||
unlock.button.advancedOptions.show=More Options
|
||||
unlock.button.advancedOptions.hide=Less Options
|
||||
@@ -89,7 +89,6 @@ unlock.savePassword.delete.confirmation.title=Delete Saved Password
|
||||
unlock.savePassword.delete.confirmation.header=Do you really want to delete the saved password of this vault?
|
||||
unlock.savePassword.delete.confirmation.content=The saved password of this vault will be immediately deleted from your system keychain. If you'd like to save your password again, you'd have to unlock your vault with the "Save Password" option enabled.
|
||||
unlock.choicebox.winDriveLetter.auto=Assign automatically
|
||||
unlock.pendingMessage.unlocking=Unlocking vault...
|
||||
unlock.errorMessage.wrongPassword=Wrong password
|
||||
unlock.errorMessage.unlockFailed=Unlock failed. See log file for details.
|
||||
unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware=Unsupported vault. This vault has been created with an older version of Cryptomator.
|
||||
|
||||
Reference in New Issue
Block a user