diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 773454e39..8a464f599 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -92,6 +92,16 @@ jobs:
* [ ] add Linux appimage, zsync file and signature file
* [ ] add Windows installer and signature file
* [ ] add MacOs disk image and signature file
+
+ ## What's new
+
+ ## Bugfixes
+
+ ## Misc
+
+ ---
+
+ :scroll: A complete list of closed issues is available [here](LINK)
draft: true
prerelease: false
- name: Upload buildkit-linux.zip to GitHub Releases
diff --git a/.gitignore b/.gitignore
index 900a21ae0..d0e6b59a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,5 +21,6 @@ pom.xml.versionsBackup
.idea/compiler.xml
.idea/encodings.xml
.idea/jarRepositories.xml
+.idea/uiDesigner.xml
.idea/**/libraries/
*.iml
\ No newline at end of file
diff --git a/README.md b/README.md
index d5cf5ea8e..dccec1091 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@ Download native binaries of Cryptomator on [cryptomator.org](https://cryptomator
## Features
-- Works with Dropbox, Google Drive, OneDrive, ownCloud, Nextcloud and any other cloud storage service which synchronizes with a local directory
+- Works with Dropbox, Google Drive, OneDrive, MEGA, pCloud, ownCloud, Nextcloud and any other cloud storage service which synchronizes with a local directory
- Open Source means: No backdoors, control is better than trust
- Client-side: No accounts, no data shared with any online service
- Totally transparent: Just work on the virtual drive as if it were a USB flash drive
diff --git a/main/buildkit/pom.xml b/main/buildkit/pom.xml
index 7ebb12112..1d13ceddb 100644
--- a/main/buildkit/pom.xml
+++ b/main/buildkit/pom.xml
@@ -4,7 +4,7 @@
org.cryptomator
main
- 1.5.14
+ 1.5.15
buildkit
pom
diff --git a/main/commons/pom.xml b/main/commons/pom.xml
index e343999ce..69de6cc8d 100644
--- a/main/commons/pom.xml
+++ b/main/commons/pom.xml
@@ -4,7 +4,7 @@
org.cryptomator
main
- 1.5.14
+ 1.5.15
commons
Cryptomator Commons
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java b/main/commons/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java
index a33dbd5ee..c998761d0 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java
+++ b/main/commons/src/main/java/org/cryptomator/common/vaults/DokanyVolume.java
@@ -5,15 +5,15 @@ import org.cryptomator.common.mountpoint.MountPointChooser;
import org.cryptomator.common.settings.VaultSettings;
import org.cryptomator.common.settings.VolumeImpl;
import org.cryptomator.cryptofs.CryptoFileSystem;
+import org.cryptomator.frontend.dokany.DokanyMountFailedException;
import org.cryptomator.frontend.dokany.Mount;
import org.cryptomator.frontend.dokany.MountFactory;
-import org.cryptomator.frontend.dokany.MountFailedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.inject.Inject;
import javax.inject.Named;
-import java.util.concurrent.ExecutorService;
+import java.util.function.Consumer;
public class DokanyVolume extends AbstractVolume {
@@ -22,15 +22,13 @@ public class DokanyVolume extends AbstractVolume {
private static final String FS_TYPE_NAME = "CryptomatorFS";
private final VaultSettings vaultSettings;
- private final MountFactory mountFactory;
private Mount mount;
@Inject
- public DokanyVolume(VaultSettings vaultSettings, ExecutorService executorService, @Named("orderedMountPointChoosers") Iterable choosers) {
+ public DokanyVolume(VaultSettings vaultSettings, @Named("orderedMountPointChoosers") Iterable choosers) {
super(choosers);
this.vaultSettings = vaultSettings;
- this.mountFactory = new MountFactory(executorService);
}
@Override
@@ -39,11 +37,11 @@ public class DokanyVolume extends AbstractVolume {
}
@Override
- public void mount(CryptoFileSystem fs, String mountFlags) throws InvalidMountPointException, VolumeException {
+ public void mount(CryptoFileSystem fs, String mountFlags, Consumer onExitAction) throws InvalidMountPointException, VolumeException {
this.mountPoint = determineMountPoint();
try {
- this.mount = mountFactory.mount(fs.getPath("/"), mountPoint, vaultSettings.mountName().get(), FS_TYPE_NAME, mountFlags.strip());
- } catch (MountFailedException e) {
+ this.mount = MountFactory.mount(fs.getPath("/"), mountPoint, vaultSettings.mountName().get(), FS_TYPE_NAME, mountFlags.strip(), onExitAction);
+ } catch (DokanyMountFailedException e) {
if (vaultSettings.getCustomMountPath().isPresent()) {
LOG.warn("Failed to mount vault into {}. Is this directory currently accessed by another process (e.g. Windows Explorer)?", mountPoint);
}
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/FuseVolume.java b/main/commons/src/main/java/org/cryptomator/common/vaults/FuseVolume.java
index 9af77408c..a1579fdaf 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/FuseVolume.java
+++ b/main/commons/src/main/java/org/cryptomator/common/vaults/FuseVolume.java
@@ -6,8 +6,8 @@ import org.cryptomator.common.mountpoint.InvalidMountPointException;
import org.cryptomator.common.mountpoint.MountPointChooser;
import org.cryptomator.common.settings.VolumeImpl;
import org.cryptomator.cryptofs.CryptoFileSystem;
-import org.cryptomator.frontend.fuse.mount.CommandFailedException;
import org.cryptomator.frontend.fuse.mount.EnvironmentVariables;
+import org.cryptomator.frontend.fuse.mount.FuseMountException;
import org.cryptomator.frontend.fuse.mount.FuseMountFactory;
import org.cryptomator.frontend.fuse.mount.FuseNotSupportedException;
import org.cryptomator.frontend.fuse.mount.Mount;
@@ -20,6 +20,7 @@ import javax.inject.Named;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
import java.util.regex.Pattern;
public class FuseVolume extends AbstractVolume {
@@ -35,13 +36,12 @@ public class FuseVolume extends AbstractVolume {
}
@Override
- public void mount(CryptoFileSystem fs, String mountFlags) throws InvalidMountPointException, VolumeException {
+ public void mount(CryptoFileSystem fs, String mountFlags, Consumer onExitAction) throws InvalidMountPointException, VolumeException {
this.mountPoint = determineMountPoint();
-
- mount(fs.getPath("/"), mountFlags);
+ mount(fs.getPath("/"), mountFlags, onExitAction);
}
- private void mount(Path root, String mountFlags) throws VolumeException {
+ private void mount(Path root, String mountFlags, Consumer onExitAction) throws VolumeException {
try {
Mounter mounter = FuseMountFactory.getMounter();
EnvironmentVariables envVars = EnvironmentVariables.create() //
@@ -49,8 +49,8 @@ public class FuseVolume extends AbstractVolume {
.withMountPoint(mountPoint) //
.withFileNameTranscoder(mounter.defaultFileNameTranscoder()) //
.build();
- this.mount = mounter.mount(root, envVars);
- } catch (CommandFailedException | FuseNotSupportedException e) {
+ this.mount = mounter.mount(root, envVars, onExitAction);
+ } catch ( FuseMountException | FuseNotSupportedException e) {
throw new VolumeException("Unable to mount Filesystem", e);
}
}
@@ -91,8 +91,7 @@ public class FuseVolume extends AbstractVolume {
public synchronized void unmountForced() throws VolumeException {
try {
mount.unmountForced();
- mount.close();
- } catch (CommandFailedException e) {
+ } catch (FuseMountException e) {
throw new VolumeException(e);
}
cleanupMountPoint();
@@ -102,8 +101,7 @@ public class FuseVolume extends AbstractVolume {
public synchronized void unmount() throws VolumeException {
try {
mount.unmount();
- mount.close();
- } catch (CommandFailedException e) {
+ } catch (FuseMountException e) {
throw new VolumeException(e);
}
cleanupMountPoint();
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/LockNotCompletedException.java b/main/commons/src/main/java/org/cryptomator/common/vaults/LockNotCompletedException.java
new file mode 100644
index 000000000..237630da0
--- /dev/null
+++ b/main/commons/src/main/java/org/cryptomator/common/vaults/LockNotCompletedException.java
@@ -0,0 +1,12 @@
+package org.cryptomator.common.vaults;
+
+public class LockNotCompletedException extends Exception {
+
+ public LockNotCompletedException(String reason) {
+ super(reason);
+ }
+
+ public LockNotCompletedException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java b/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java
index 98a6cf21b..319272687 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java
+++ b/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java
@@ -42,6 +42,7 @@ import java.util.EnumSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
@@ -56,7 +57,7 @@ public class Vault {
private final Provider volumeProvider;
private final StringBinding defaultMountFlags;
private final AtomicReference cryptoFileSystem;
- private final ObjectProperty state;
+ private final VaultState state;
private final ObjectProperty lastKnownException;
private final VaultStats stats;
private final StringBinding displayName;
@@ -74,7 +75,7 @@ public class Vault {
private volatile Volume volume;
@Inject
- Vault(VaultSettings vaultSettings, Provider volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference cryptoFileSystem, ObjectProperty state, @Named("lastKnownException") ObjectProperty lastKnownException, VaultStats stats) {
+ Vault(VaultSettings vaultSettings, Provider volumeProvider, @DefaultMountFlags StringBinding defaultMountFlags, AtomicReference cryptoFileSystem, VaultState state, @Named("lastKnownException") ObjectProperty lastKnownException, VaultStats stats) {
this.vaultSettings = vaultSettings;
this.volumeProvider = volumeProvider;
this.defaultMountFlags = defaultMountFlags;
@@ -104,19 +105,27 @@ public class Vault {
if (vaultSettings.usesReadOnlyMode().get()) {
flags.add(FileSystemFlags.READONLY);
}
- if (!flags.contains(FileSystemFlags.READONLY) && vaultSettings.filenameLengthLimit().get() == -1) {
+
+ int usedFilenameLengthLimit;
+ var fileSystemCapabilityChecker = new FileSystemCapabilityChecker();
+ if (flags.contains(FileSystemFlags.READONLY)) {
+ usedFilenameLengthLimit = Constants.MAX_CIPHERTEXT_NAME_LENGTH;
+ } else if (vaultSettings.filenameLengthLimit().get() == -1) {
LOG.debug("Determining file name length limitations...");
- int limit = new FileSystemCapabilityChecker().determineSupportedFileNameLength(getPath());
- vaultSettings.filenameLengthLimit().set(limit);
- LOG.info("Storing file name length limit of {}", limit);
+ usedFilenameLengthLimit = fileSystemCapabilityChecker.determineSupportedFileNameLength(getPath());
+ vaultSettings.filenameLengthLimit().set(usedFilenameLengthLimit);
+ LOG.info("Storing file name length limit of {}", usedFilenameLengthLimit);
+ } else {
+ usedFilenameLengthLimit = vaultSettings.filenameLengthLimit().get();
}
- assert vaultSettings.filenameLengthLimit().get() > 0;
+
+ assert usedFilenameLengthLimit > 0;
CryptoFileSystemProperties fsProps = CryptoFileSystemProperties.cryptoFileSystemProperties() //
.withPassphrase(passphrase) //
.withFlags(flags) //
.withMasterkeyFilename(MASTERKEY_FILENAME) //
.withMaxPathLength(vaultSettings.filenameLengthLimit().get() + Constants.MAX_ADDITIONAL_PATH_LENGTH) //
- .withMaxNameLength(vaultSettings.filenameLengthLimit().get()) //
+ .withMaxNameLength(usedFilenameLengthLimit) //
.build();
return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps);
}
@@ -139,7 +148,7 @@ public class Vault {
cryptoFileSystem.set(fs);
try {
volume = volumeProvider.get();
- volume.mount(fs, getEffectiveMountFlags());
+ volume.mount(fs, getEffectiveMountFlags(), this::lockOnVolumeExit);
} catch (Exception e) {
destroyCryptoFileSystem();
throw e;
@@ -149,13 +158,33 @@ public class Vault {
}
}
- public synchronized void lock(boolean forced) throws VolumeException {
+ private void lockOnVolumeExit(Throwable t) {
+ LOG.info("Unmounted vault '{}'", getDisplayName());
+ destroyCryptoFileSystem();
+ state.set(VaultState.Value.LOCKED);
+ if (t != null) {
+ LOG.warn("Unexpected unmount and lock of vault " + getDisplayName(), t);
+ }
+ }
+
+ public synchronized void lock(boolean forced) throws VolumeException, LockNotCompletedException {
+ //initiate unmount
if (forced && volume.supportsForcedUnmount()) {
volume.unmountForced();
} else {
volume.unmount();
}
- destroyCryptoFileSystem();
+
+ //wait for lockOnVolumeExit to be executed
+ try {
+ boolean locked = state.awaitState(VaultState.Value.LOCKED, 3000, TimeUnit.MILLISECONDS);
+ if (!locked) {
+ throw new LockNotCompletedException("Locking of vault " + this.getDisplayName() + " still in progress.");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new LockNotCompletedException(e);
+ }
}
public void reveal(Volume.Revealer vaultRevealer) throws VolumeException {
@@ -166,16 +195,12 @@ public class Vault {
// Observable Properties
// *******************************************************************************
- public ObjectProperty stateProperty() {
+ public VaultState stateProperty() {
return state;
}
- public VaultState getState() {
- return state.get();
- }
-
- public void setState(VaultState value) {
- state.setValue(value);
+ public VaultState.Value getState() {
+ return state.getValue();
}
public ObjectProperty lastKnownExceptionProperty() {
@@ -195,7 +220,7 @@ public class Vault {
}
public boolean isLocked() {
- return state.get() == VaultState.LOCKED;
+ return state.get() == VaultState.Value.LOCKED;
}
public BooleanBinding processingProperty() {
@@ -203,7 +228,7 @@ public class Vault {
}
public boolean isProcessing() {
- return state.get() == VaultState.PROCESSING;
+ return state.get() == VaultState.Value.PROCESSING;
}
public BooleanBinding unlockedProperty() {
@@ -211,7 +236,7 @@ public class Vault {
}
public boolean isUnlocked() {
- return state.get() == VaultState.UNLOCKED;
+ return state.get() == VaultState.Value.UNLOCKED;
}
public BooleanBinding missingProperty() {
@@ -219,7 +244,7 @@ public class Vault {
}
public boolean isMissing() {
- return state.get() == VaultState.MISSING;
+ return state.get() == VaultState.Value.MISSING;
}
public BooleanBinding needsMigrationProperty() {
@@ -227,7 +252,7 @@ public class Vault {
}
public boolean isNeedsMigration() {
- return state.get() == VaultState.NEEDS_MIGRATION;
+ return state.get() == VaultState.Value.NEEDS_MIGRATION;
}
public BooleanBinding unknownErrorProperty() {
@@ -235,7 +260,7 @@ public class Vault {
}
public boolean isUnknownError() {
- return state.get() == VaultState.ERROR;
+ return state.get() == VaultState.Value.ERROR;
}
public StringBinding displayNameProperty() {
@@ -251,7 +276,7 @@ public class Vault {
}
public String getAccessPoint() {
- if (state.get() == VaultState.UNLOCKED) {
+ if (state.getValue() == VaultState.Value.UNLOCKED) {
assert volume != null;
return volume.getMountPoint().orElse(Path.of("")).toString();
} else {
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultComponent.java b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultComponent.java
index debfab3ed..47be62520 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultComponent.java
+++ b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultComponent.java
@@ -26,7 +26,7 @@ public interface VaultComponent {
Builder vaultSettings(VaultSettings vaultSettings);
@BindsInstance
- Builder initialVaultState(VaultState vaultState);
+ Builder initialVaultState(VaultState.Value vaultState);
@BindsInstance
Builder initialErrorCause(@Nullable @Named("lastKnownException") Exception initialErrorCause);
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java
index b7c10a4d4..4b8ca71e8 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java
+++ b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultListManager.java
@@ -25,7 +25,6 @@ import java.nio.file.Path;
import java.util.Collection;
import java.util.Optional;
import java.util.ResourceBundle;
-import java.util.stream.Collectors;
import static org.cryptomator.common.Constants.MASTERKEY_FILENAME;
@@ -94,42 +93,43 @@ public class VaultListManager {
private Vault create(VaultSettings vaultSettings) {
VaultComponent.Builder compBuilder = vaultComponentBuilder.vaultSettings(vaultSettings);
try {
- VaultState vaultState = determineVaultState(vaultSettings.path().get());
+ VaultState.Value vaultState = determineVaultState(vaultSettings.path().get());
compBuilder.initialVaultState(vaultState);
} catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vaultSettings.path().get(), e);
- compBuilder.initialVaultState(VaultState.ERROR);
+ compBuilder.initialVaultState(VaultState.Value.ERROR);
compBuilder.initialErrorCause(e);
}
return compBuilder.build().vault();
}
- public static VaultState redetermineVaultState(Vault vault) {
- VaultState previousState = vault.getState();
+ public static VaultState.Value redetermineVaultState(Vault vault) {
+ VaultState state = vault.stateProperty();
+ VaultState.Value previousState = state.getValue();
return switch (previousState) {
case LOCKED, NEEDS_MIGRATION, MISSING -> {
try {
- VaultState determinedState = determineVaultState(vault.getPath());
- vault.setState(determinedState);
+ VaultState.Value determinedState = determineVaultState(vault.getPath());
+ state.set(determinedState);
yield determinedState;
} catch (IOException e) {
LOG.warn("Failed to determine vault state for " + vault.getPath(), e);
- vault.setState(VaultState.ERROR);
+ state.set(VaultState.Value.ERROR);
vault.setLastKnownException(e);
- yield VaultState.ERROR;
+ yield VaultState.Value.ERROR;
}
}
case ERROR, UNLOCKED, PROCESSING -> previousState;
};
}
- private static VaultState determineVaultState(Path pathToVault) throws IOException {
+ private static VaultState.Value determineVaultState(Path pathToVault) throws IOException {
if (!CryptoFileSystemProvider.containsVault(pathToVault, MASTERKEY_FILENAME)) {
- return VaultState.MISSING;
+ return VaultState.Value.MISSING;
} else if (Migrators.get().needsMigration(pathToVault, MASTERKEY_FILENAME)) {
- return VaultState.NEEDS_MIGRATION;
+ return VaultState.Value.NEEDS_MIGRATION;
} else {
- return VaultState.LOCKED;
+ return VaultState.Value.LOCKED;
}
}
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java
index 56a2814cf..b6e070310 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java
+++ b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultModule.java
@@ -40,12 +40,6 @@ public class VaultModule {
return new AtomicReference<>();
}
- @Provides
- @PerVault
- public ObjectProperty provideVaultState(VaultState initialState) {
- return new SimpleObjectProperty<>(initialState);
- }
-
@Provides
@Named("lastKnownException")
@PerVault
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultState.java b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultState.java
index fa5e6c295..801ea7653 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultState.java
+++ b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultState.java
@@ -1,34 +1,141 @@
package org.cryptomator.common.vaults;
-public enum VaultState {
- /**
- * No vault found at the provided path
- */
- MISSING,
+import com.google.common.base.Preconditions;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.inject.Inject;
+import javafx.application.Platform;
+import javafx.beans.value.ObservableObjectValue;
+import javafx.beans.value.ObservableValueBase;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+@PerVault
+public class VaultState extends ObservableValueBase implements ObservableObjectValue {
+
+ private static final Logger LOG = LoggerFactory.getLogger(VaultState.class);
+
+ public enum Value {
+ /**
+ * No vault found at the provided path
+ */
+ MISSING,
+
+ /**
+ * Vault requires migration to a newer vault format
+ */
+ NEEDS_MIGRATION,
+
+ /**
+ * Vault ready to be unlocked
+ */
+ LOCKED,
+
+ /**
+ * Vault in transition between two other states
+ */
+ PROCESSING,
+
+ /**
+ * Vault is unlocked
+ */
+ UNLOCKED,
+
+ /**
+ * Unknown state due to preceeding unrecoverable exceptions.
+ */
+ ERROR;
+ }
+
+ private final AtomicReference value;
+ private final Lock lock = new ReentrantLock();
+ private final Condition valueChanged = lock.newCondition();
+
+ @Inject
+ public VaultState(VaultState.Value initialValue) {
+ this.value = new AtomicReference<>(initialValue);
+ }
+
+ @Override
+ public Value get() {
+ return getValue();
+ }
+
+ @Override
+ public Value getValue() {
+ return value.get();
+ }
/**
- * Vault requires migration to a newer vault format
+ * Transitions from fromState to toState.
+ *
+ * @param fromState Previous state
+ * @param toState New state
+ * @return true if successful
*/
- NEEDS_MIGRATION,
+ public boolean transition(Value fromState, Value toState) {
+ Preconditions.checkArgument(fromState != toState, "fromState must be different than toState");
+ boolean success = value.compareAndSet(fromState, toState);
+ if (success) {
+ fireValueChangedEvent();
+ } else {
+ LOG.debug("Failed transiting into state {}: Expected state was not{}.", fromState, toState);
+ }
+ return success;
+ }
+
+ public void set(Value newState) {
+ var oldState = value.getAndSet(newState);
+ if (oldState != newState) {
+ fireValueChangedEvent();
+ }
+ }
/**
- * Vault ready to be unlocked
+ * Waits for the specified time, until the desired state is reached.
+ *
+ * @param desiredState what state to wait for
+ * @param time the maximum time to wait
+ * @param unit the time unit of the {@code time} argument
+ * @return {@code false} if the waiting time detectably elapsed before reaching {@code desiredState}
+ * @throws InterruptedException if the current thread is interrupted
*/
- LOCKED,
+ public boolean awaitState(Value desiredState, long time, TimeUnit unit) throws InterruptedException {
+ lock.lock();
+ try {
+ long remaining = TimeUnit.NANOSECONDS.convert(time, unit);
+ while (value.get() != desiredState) {
+ if (remaining <= 0L) {
+ return false;
+ }
+ remaining = valueChanged.awaitNanos(remaining);
+ }
+ return true;
+ } finally {
+ lock.unlock();
+ }
+ }
- /**
- * Vault in transition between two other states
- */
- PROCESSING,
-
- /**
- * Vault is unlocked
- */
- UNLOCKED,
-
- /**
- * Unknown state due to preceeding unrecoverable exceptions.
- */
- ERROR;
+ private void signal() {
+ lock.lock();
+ try {
+ valueChanged.signalAll();
+ } finally {
+ lock.unlock();
+ }
+ }
+ @Override
+ protected void fireValueChangedEvent() {
+ signal();
+ if (Platform.isFxApplicationThread()) {
+ super.fireValueChangedEvent();
+ } else {
+ Platform.runLater(super::fireValueChangedEvent);
+ }
+ }
}
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultStats.java b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultStats.java
index 46cf31991..6dc86e8be 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/VaultStats.java
+++ b/main/commons/src/main/java/org/cryptomator/common/vaults/VaultStats.java
@@ -26,7 +26,7 @@ public class VaultStats {
private static final Logger LOG = LoggerFactory.getLogger(VaultStats.class);
private final AtomicReference fs;
- private final ObjectProperty state;
+ private final VaultState state;
private final ScheduledService> updateService;
private final LongProperty bytesPerSecondRead = new SimpleLongProperty();
private final LongProperty bytesPerSecondWritten = new SimpleLongProperty();
@@ -41,7 +41,7 @@ public class VaultStats {
private final LongProperty filesWritten = new SimpleLongProperty();
@Inject
- VaultStats(AtomicReference fs, ObjectProperty state, ExecutorService executor) {
+ VaultStats(AtomicReference fs, VaultState state, ExecutorService executor) {
this.fs = fs;
this.state = state;
this.updateService = new UpdateStatsService();
@@ -52,13 +52,13 @@ public class VaultStats {
}
private void vaultStateChanged(@SuppressWarnings("unused") Observable observable) {
- if (VaultState.UNLOCKED.equals(state.get())) {
+ if (VaultState.Value.UNLOCKED == state.get()) {
assert fs.get() != null;
LOG.debug("start recording stats");
- updateService.restart();
+ Platform.runLater(() -> updateService.restart());
} else {
LOG.debug("stop recording stats");
- updateService.cancel();
+ Platform.runLater(() -> updateService.cancel());
}
}
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/Volume.java b/main/commons/src/main/java/org/cryptomator/common/vaults/Volume.java
index 74a307d5a..f608122bf 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/Volume.java
+++ b/main/commons/src/main/java/org/cryptomator/common/vaults/Volume.java
@@ -7,6 +7,8 @@ import org.cryptomator.cryptofs.CryptoFileSystem;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Optional;
+import java.util.concurrent.CompletionStage;
+import java.util.function.Consumer;
import java.util.stream.Stream;
/**
@@ -32,7 +34,7 @@ public interface Volume {
* @param fs
* @throws IOException
*/
- void mount(CryptoFileSystem fs, String mountFlags) throws IOException, VolumeException, InvalidMountPointException;
+ void mount(CryptoFileSystem fs, String mountFlags, Consumer onExitAction) throws IOException, VolumeException, InvalidMountPointException;
/**
* Reveals the mounted volume.
diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java b/main/commons/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java
index 8b4f27fdb..03c83377c 100644
--- a/main/commons/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java
+++ b/main/commons/src/main/java/org/cryptomator/common/vaults/WebDavVolume.java
@@ -17,6 +17,7 @@ import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.file.Path;
import java.util.Optional;
+import java.util.function.Consumer;
import java.util.function.Supplier;
public class WebDavVolume implements Volume {
@@ -31,6 +32,7 @@ public class WebDavVolume implements Volume {
private WebDavServer server;
private WebDavServletController servlet;
private Mounter.Mount mount;
+ private Consumer onExitAction;
@Inject
public WebDavVolume(Provider serverProvider, VaultSettings vaultSettings, Settings settings, WindowsDriveLetters windowsDriveLetters) {
@@ -41,12 +43,13 @@ public class WebDavVolume implements Volume {
}
@Override
- public void mount(CryptoFileSystem fs, String mountFlags) throws VolumeException {
+ public void mount(CryptoFileSystem fs, String mountFlags, Consumer onExitAction) throws VolumeException {
startServlet(fs);
mountServlet();
+ this.onExitAction = onExitAction;
}
- private void startServlet(CryptoFileSystem fs){
+ private void startServlet(CryptoFileSystem fs) {
if (server == null) {
server = serverProvider.get();
}
@@ -66,7 +69,7 @@ public class WebDavVolume implements Volume {
//on windows, prevent an automatic drive letter selection in the upstream library. Either we choose already a specifc one or there is no free.
Supplier driveLetterSupplier;
- if(System.getProperty("os.name").toLowerCase().contains("windows") && vaultSettings.winDriveLetter().isEmpty().get()) {
+ if (System.getProperty("os.name").toLowerCase().contains("windows") && vaultSettings.winDriveLetter().isEmpty().get()) {
driveLetterSupplier = () -> windowsDriveLetters.getAvailableDriveLetter().orElse(null);
} else {
driveLetterSupplier = () -> vaultSettings.winDriveLetter().get();
@@ -101,6 +104,7 @@ public class WebDavVolume implements Volume {
throw new VolumeException(e);
}
cleanup();
+ onExitAction.accept(null);
}
@Override
@@ -111,6 +115,7 @@ public class WebDavVolume implements Volume {
throw new VolumeException(e);
}
cleanup();
+ onExitAction.accept(null);
}
@Override
diff --git a/main/launcher/pom.xml b/main/launcher/pom.xml
index 64e142c23..adda3f1ca 100644
--- a/main/launcher/pom.xml
+++ b/main/launcher/pom.xml
@@ -4,7 +4,7 @@
org.cryptomator
main
- 1.5.14
+ 1.5.15
launcher
Cryptomator Launcher
diff --git a/main/pom.xml b/main/pom.xml
index a740a1427..7365642b8 100644
--- a/main/pom.xml
+++ b/main/pom.xml
@@ -3,7 +3,7 @@
4.0.0
org.cryptomator
main
- 1.5.14
+ 1.5.15
pom
Cryptomator
@@ -30,12 +30,12 @@
1.0.0-beta2
1.0.0-beta2
1.0.0-beta1
- 1.3.0
- 1.2.4
+ 1.3.1
+ 1.3.0
1.2.0
- 15
+ 16
3.11
3.13.0
2.1.0
diff --git a/main/ui/pom.xml b/main/ui/pom.xml
index 1332596e8..680afa735 100644
--- a/main/ui/pom.xml
+++ b/main/ui/pom.xml
@@ -4,7 +4,7 @@
org.cryptomator
main
- 1.5.14
+ 1.5.15
ui
Cryptomator GUI
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java
index 4eeea5c3d..39e25d503 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultLocationController.java
@@ -1,10 +1,10 @@
package org.cryptomator.ui.addvaultwizard;
import dagger.Lazy;
-import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxController;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
+import org.cryptomator.ui.controls.FontAwesome5IconView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -15,21 +15,21 @@ import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
+import javafx.scene.Node;
import javafx.scene.Scene;
+import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleGroup;
import javafx.stage.DirectoryChooser;
import javafx.stage.Stage;
import java.io.File;
-import java.io.IOException;
-import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ResourceBundle;
@@ -43,55 +43,74 @@ public class CreateNewVaultLocationController implements FxController {
private final Stage window;
private final Lazy chooseNameScene;
private final Lazy choosePasswordScene;
- private final ErrorComponent.Builder errorComponent;
private final LocationPresets locationPresets;
private final ObjectProperty vaultPath;
private final StringProperty vaultName;
private final ResourceBundle resourceBundle;
private final BooleanBinding validVaultPath;
private final BooleanProperty usePresetPath;
- private final StringProperty warningText;
+ private final StringProperty statusText;
+ private final ObjectProperty statusGraphic;
private Path customVaultPath = DEFAULT_CUSTOM_VAULT_PATH;
+
+ //FXML
public ToggleGroup predefinedLocationToggler;
public RadioButton iclouddriveRadioButton;
public RadioButton dropboxRadioButton;
public RadioButton gdriveRadioButton;
public RadioButton onedriveRadioButton;
+ public RadioButton megaRadioButton;
+ public RadioButton pcloudRadioButton;
public RadioButton customRadioButton;
+ public Label vaultPathStatus;
+ public FontAwesome5IconView goodLocation;
+ public FontAwesome5IconView badLocation;
@Inject
- CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy choosePasswordScene, ErrorComponent.Builder errorComponent, LocationPresets locationPresets, ObjectProperty vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
+ CreateNewVaultLocationController(@AddVaultWizardWindow Stage window, @FxmlScene(FxmlFile.ADDVAULT_NEW_NAME) Lazy chooseNameScene, @FxmlScene(FxmlFile.ADDVAULT_NEW_PASSWORD) Lazy choosePasswordScene, LocationPresets locationPresets, ObjectProperty vaultPath, @Named("vaultName") StringProperty vaultName, ResourceBundle resourceBundle) {
this.window = window;
this.chooseNameScene = chooseNameScene;
this.choosePasswordScene = choosePasswordScene;
- this.errorComponent = errorComponent;
this.locationPresets = locationPresets;
this.vaultPath = vaultPath;
this.vaultName = vaultName;
this.resourceBundle = resourceBundle;
- this.validVaultPath = Bindings.createBooleanBinding(this::isValidVaultPath, vaultPath);
+ this.validVaultPath = Bindings.createBooleanBinding(this::validateVaultPathAndSetStatus, this.vaultPath);
this.usePresetPath = new SimpleBooleanProperty();
- this.warningText = new SimpleStringProperty();
+ this.statusText = new SimpleStringProperty();
+ this.statusGraphic = new SimpleObjectProperty<>();
}
- private boolean isValidVaultPath() {
- return vaultPath.get() != null && Files.notExists(vaultPath.get());
+ private boolean validateVaultPathAndSetStatus() {
+ final Path p = vaultPath.get();
+ if (p == null) {
+ statusText.set("Error: Path is NULL.");
+ statusGraphic.set(badLocation);
+ return false;
+ } else if (!Files.exists(p.getParent())) {
+ statusText.set(resourceBundle.getString("addvaultwizard.new.locationDoesNotExist"));
+ statusGraphic.set(badLocation);
+ return false;
+ } else if (!Files.isWritable(p.getParent())) {
+ statusText.set(resourceBundle.getString("addvaultwizard.new.locationIsNotWritable"));
+ statusGraphic.set(badLocation);
+ return false;
+ } else if (!Files.notExists(p)) {
+ statusText.set(resourceBundle.getString("addvaultwizard.new.fileAlreadyExists"));
+ statusGraphic.set(badLocation);
+ return false;
+ } else {
+ statusText.set(resourceBundle.getString("addvaultwizard.new.locationIsOk"));
+ statusGraphic.set(goodLocation);
+ return true;
+ }
}
@FXML
public void initialize() {
predefinedLocationToggler.selectedToggleProperty().addListener(this::togglePredefinedLocation);
usePresetPath.bind(predefinedLocationToggler.selectedToggleProperty().isNotEqualTo(customRadioButton));
- vaultPath.addListener(this::vaultPathDidChange);
- }
-
- private void vaultPathDidChange(@SuppressWarnings("unused") ObservableValue extends Path> observable, @SuppressWarnings("unused") Path oldValue, Path newValue) {
- if (!Files.notExists(newValue)) {
- warningText.set(resourceBundle.getString("addvaultwizard.new.fileAlreadyExists"));
- } else {
- warningText.set(null);
- }
}
private void togglePredefinedLocation(@SuppressWarnings("unused") ObservableValue extends Toggle> observable, @SuppressWarnings("unused") Toggle oldValue, Toggle newValue) {
@@ -103,6 +122,10 @@ public class CreateNewVaultLocationController implements FxController {
vaultPath.set(locationPresets.getGdriveLocation().resolve(vaultName.get()));
} else if (onedriveRadioButton.equals(newValue)) {
vaultPath.set(locationPresets.getOnedriveLocation().resolve(vaultName.get()));
+ } else if (megaRadioButton.equals(newValue)) {
+ vaultPath.set(locationPresets.getMegaLocation().resolve(vaultName.get()));
+ } else if (pcloudRadioButton.equals(newValue)) {
+ vaultPath.set(locationPresets.getPcloudLocation().resolve(vaultName.get()));
} else if (customRadioButton.equals(newValue)) {
vaultPath.set(customVaultPath.resolve(vaultName.get()));
}
@@ -115,21 +138,10 @@ public class CreateNewVaultLocationController implements FxController {
@FXML
public void next() {
- try {
- // check if we have write access AND the vaultPath doesn't already exist:
- assert Files.isDirectory(vaultPath.get().getParent());
- Path createdDir = Files.createDirectory(vaultPath.get());
- Files.delete(createdDir); // assert: dir exists and is empty
+ if (validateVaultPathAndSetStatus()) {
window.setScene(choosePasswordScene.get());
- } catch (FileAlreadyExistsException e) {
- LOG.warn("Can not use already existing vault path {}", vaultPath.get());
- warningText.set(resourceBundle.getString("addvaultwizard.new.fileAlreadyExists"));
- } catch (NoSuchFileException e) {
- LOG.warn("At least one path component does not exist of path {}", vaultPath.get());
- warningText.set(resourceBundle.getString("addvaultwizard.new.locationDoesNotExist"));
- } catch (IOException e) {
- LOG.error("Failed to create and delete directory at chosen vault path.", e);
- errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
+ } else {
+ validVaultPath.invalidate();
}
}
@@ -179,19 +191,27 @@ public class CreateNewVaultLocationController implements FxController {
return usePresetPath.get();
}
- public StringProperty warningTextProperty() {
- return warningText;
+ public BooleanBinding anyRadioButtonSelectedProperty() {
+ return predefinedLocationToggler.selectedToggleProperty().isNotNull();
}
- public String getWarningText() {
- return warningText.get();
+ public boolean isAnyRadioButtonSelected() {
+ return anyRadioButtonSelectedProperty().get();
}
- public BooleanBinding showWarningProperty() {
- return warningText.isNotEmpty();
+ public StringProperty statusTextProperty() {
+ return statusText;
}
- public boolean isShowWarning() {
- return showWarningProperty().get();
+ public String getStatusText() {
+ return statusText.get();
+ }
+
+ public ObjectProperty statusGraphicProperty() {
+ return statusGraphic;
+ }
+
+ public Node getStatusGraphic() {
+ return statusGraphic.get();
}
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/LocationPresets.java b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/LocationPresets.java
index b9f796043..6cec655cb 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/LocationPresets.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/LocationPresets.java
@@ -16,15 +16,21 @@ public class LocationPresets {
private static final String[] DROPBOX_LOCATIONS = {"~/Dropbox"};
private static final String[] GDRIVE_LOCATIONS = {"~/Google Drive"};
private static final String[] ONEDRIVE_LOCATIONS = {"~/OneDrive"};
+ private static final String[] MEGA_LOCATIONS = {"~/MEGA"};
+ private static final String[] PCLOUD_LOCATIONS = {"~/pCloudDrive"};
private final ReadOnlyObjectProperty iclouddriveLocation;
private final ReadOnlyObjectProperty dropboxLocation;
private final ReadOnlyObjectProperty gdriveLocation;
private final ReadOnlyObjectProperty onedriveLocation;
+ private final ReadOnlyObjectProperty megaLocation;
+ private final ReadOnlyObjectProperty pcloudLocation;
private final BooleanBinding foundIclouddrive;
private final BooleanBinding foundDropbox;
private final BooleanBinding foundGdrive;
private final BooleanBinding foundOnedrive;
+ private final BooleanBinding foundMega;
+ private final BooleanBinding foundPcloud;
@Inject
public LocationPresets() {
@@ -32,10 +38,14 @@ public class LocationPresets {
this.dropboxLocation = new SimpleObjectProperty<>(existingWritablePath(DROPBOX_LOCATIONS));
this.gdriveLocation = new SimpleObjectProperty<>(existingWritablePath(GDRIVE_LOCATIONS));
this.onedriveLocation = new SimpleObjectProperty<>(existingWritablePath(ONEDRIVE_LOCATIONS));
+ this.megaLocation = new SimpleObjectProperty<>(existingWritablePath(MEGA_LOCATIONS));
+ this.pcloudLocation = new SimpleObjectProperty<>(existingWritablePath(PCLOUD_LOCATIONS));
this.foundIclouddrive = iclouddriveLocation.isNotNull();
this.foundDropbox = dropboxLocation.isNotNull();
this.foundGdrive = gdriveLocation.isNotNull();
this.foundOnedrive = onedriveLocation.isNotNull();
+ this.foundMega = megaLocation.isNotNull();
+ this.foundPcloud = pcloudLocation.isNotNull();
}
private static Path existingWritablePath(String... candidates) {
@@ -122,4 +132,36 @@ public class LocationPresets {
return foundOnedrive.get();
}
+ public ReadOnlyObjectProperty megaLocationProperty() {
+ return megaLocation;
+ }
+
+ public Path getMegaLocation() {
+ return megaLocation.get();
+ }
+
+ public BooleanBinding foundMegaProperty() {
+ return foundMega;
+ }
+
+ public boolean isFoundMega() {
+ return foundMega.get();
+ }
+
+ public ReadOnlyObjectProperty pcloudLocationProperty() {
+ return pcloudLocation;
+ }
+
+ public Path getPcloudLocation() {
+ return pcloudLocation.get();
+ }
+
+ public BooleanBinding foundPcloudProperty() {
+ return foundPcloud;
+ }
+
+ public boolean isFoundPcloud() {
+ return foundPcloud.get();
+ }
+
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java b/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java
index 57393b858..b81ddec49 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/common/VaultService.java
@@ -1,5 +1,6 @@
package org.cryptomator.ui.common;
+import org.cryptomator.common.vaults.LockNotCompletedException;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.common.vaults.Volume;
@@ -175,24 +176,29 @@ public class VaultService {
}
@Override
- protected Vault call() throws Volume.VolumeException {
+ protected Vault call() throws Volume.VolumeException, LockNotCompletedException {
vault.lock(forced);
return vault;
}
@Override
protected void scheduled() {
- vault.setState(VaultState.PROCESSING);
+ vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING);
}
@Override
protected void succeeded() {
- vault.setState(VaultState.LOCKED);
+ vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
}
@Override
protected void failed() {
- vault.setState(VaultState.UNLOCKED);
+ vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.UNLOCKED);
+ }
+
+ @Override
+ protected void cancelled() {
+ vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.UNLOCKED);
}
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java
index 917e704fc..b0ea8da47 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/fxapp/FxApplication.java
@@ -5,11 +5,13 @@ import org.cryptomator.common.LicenseHolder;
import org.cryptomator.common.settings.Settings;
import org.cryptomator.common.settings.UiTheme;
import org.cryptomator.common.vaults.Vault;
+import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.integrations.tray.TrayIntegrationProvider;
import org.cryptomator.integrations.uiappearance.Theme;
import org.cryptomator.integrations.uiappearance.UiAppearanceException;
import org.cryptomator.integrations.uiappearance.UiAppearanceListener;
import org.cryptomator.integrations.uiappearance.UiAppearanceProvider;
+import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.VaultService;
import org.cryptomator.ui.lock.LockComponent;
import org.cryptomator.ui.mainwindow.MainWindowComponent;
@@ -44,8 +46,9 @@ public class FxApplication extends Application {
private final Lazy mainWindow;
private final Lazy preferencesWindow;
private final Lazy quitWindow;
- private final Provider unlockWindowBuilderProvider;
- private final Provider lockWindowBuilderProvider;
+ private final Provider unlockWorkflowBuilderProvider;
+ private final Provider lockWorkflowBuilderProvider;
+ private final ErrorComponent.Builder errorWindowBuilder;
private final Optional trayIntegration;
private final Optional appearanceProvider;
private final VaultService vaultService;
@@ -55,13 +58,14 @@ public class FxApplication extends Application {
private final UiAppearanceListener systemInterfaceThemeListener = this::systemInterfaceThemeChanged;
@Inject
- FxApplication(Settings settings, Lazy mainWindow, Lazy preferencesWindow, Provider unlockWindowBuilderProvider, Provider lockWindowBuilderProvider, Lazy quitWindow, Optional trayIntegration, Optional appearanceProvider, VaultService vaultService, LicenseHolder licenseHolder) {
+ FxApplication(Settings settings, Lazy mainWindow, Lazy preferencesWindow, Provider unlockWorkflowBuilderProvider, Provider lockWorkflowBuilderProvider, Lazy quitWindow, ErrorComponent.Builder errorWindowBuilder, Optional trayIntegration, Optional appearanceProvider, VaultService vaultService, LicenseHolder licenseHolder) {
this.settings = settings;
this.mainWindow = mainWindow;
this.preferencesWindow = preferencesWindow;
- this.unlockWindowBuilderProvider = unlockWindowBuilderProvider;
- this.lockWindowBuilderProvider = lockWindowBuilderProvider;
+ this.unlockWorkflowBuilderProvider = unlockWorkflowBuilderProvider;
+ this.lockWorkflowBuilderProvider = lockWorkflowBuilderProvider;
this.quitWindow = quitWindow;
+ this.errorWindowBuilder = errorWindowBuilder;
this.trayIntegration = trayIntegration;
this.appearanceProvider = appearanceProvider;
this.vaultService = vaultService;
@@ -113,15 +117,23 @@ public class FxApplication extends Application {
public void startUnlockWorkflow(Vault vault, Optional owner) {
Platform.runLater(() -> {
- unlockWindowBuilderProvider.get().vault(vault).owner(owner).build().startUnlockWorkflow();
- LOG.debug("Showing UnlockWindow for {}", vault.getDisplayName());
+ if (vault.stateProperty().transition(VaultState.Value.LOCKED, VaultState.Value.PROCESSING)) {
+ unlockWorkflowBuilderProvider.get().vault(vault).owner(owner).build().startUnlockWorkflow();
+ LOG.debug("Start unlock workflow for {}", vault.getDisplayName());
+ } else {
+ showMainWindow().thenAccept(mainWindow -> errorWindowBuilder.window(mainWindow).cause(new IllegalStateException("Unable to unlock vault in non-locked state.")));
+ }
});
}
public void startLockWorkflow(Vault vault, Optional owner) {
Platform.runLater(() -> {
- lockWindowBuilderProvider.get().vault(vault).owner(owner).build().startLockWorkflow();
- LOG.debug("Start lock workflow for {}", vault.getDisplayName());
+ if (vault.stateProperty().transition(VaultState.Value.UNLOCKED, VaultState.Value.PROCESSING)) {
+ lockWorkflowBuilderProvider.get().vault(vault).owner(owner).build().startLockWorkflow();
+ LOG.debug("Start lock workflow for {}", vault.getDisplayName());
+ } else {
+ showMainWindow().thenAccept(mainWindow -> errorWindowBuilder.window(mainWindow).cause(new IllegalStateException("Unable to lock vault in non-unlocked state.")));
+ }
});
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/launcher/AppLifecycleListener.java b/main/ui/src/main/java/org/cryptomator/ui/launcher/AppLifecycleListener.java
index 646c3ce6b..75af409f1 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/launcher/AppLifecycleListener.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/launcher/AppLifecycleListener.java
@@ -1,6 +1,7 @@
package org.cryptomator.ui.launcher;
import org.cryptomator.common.ShutdownHook;
+import org.cryptomator.common.vaults.LockNotCompletedException;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.common.vaults.Volume;
@@ -24,11 +25,13 @@ import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
+import static org.cryptomator.common.vaults.VaultState.Value.*;
+
@Singleton
public class AppLifecycleListener {
private static final Logger LOG = LoggerFactory.getLogger(AppLifecycleListener.class);
- public static final Set STATES_ALLOWING_TERMINATION = EnumSet.of(VaultState.LOCKED, VaultState.NEEDS_MIGRATION, VaultState.MISSING, VaultState.ERROR);
+ public static final Set STATES_ALLOWING_TERMINATION = EnumSet.of(LOCKED, NEEDS_MIGRATION, MISSING, ERROR);
private final FxApplicationStarter fxApplicationStarter;
private final CountDownLatch shutdownLatch;
@@ -127,6 +130,8 @@ public class AppLifecycleListener {
vault.lock(true);
} catch (Volume.VolumeException e) {
LOG.error("Failed to unmount vault " + vault.getPath(), e);
+ } catch (LockNotCompletedException e) {
+ LOG.error("Failed to lock vault " + vault.getPath(), e);
}
}
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java b/main/ui/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java
index acb6d1355..87fd486f2 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/lock/LockWorkflow.java
@@ -1,9 +1,11 @@
package org.cryptomator.ui.lock;
import dagger.Lazy;
+import org.cryptomator.common.vaults.LockNotCompletedException;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultState;
import org.cryptomator.common.vaults.Volume;
+import org.cryptomator.ui.common.ErrorComponent;
import org.cryptomator.ui.common.FxmlFile;
import org.cryptomator.ui.common.FxmlScene;
import org.cryptomator.ui.common.UserInteractionLock;
@@ -35,21 +37,23 @@ public class LockWorkflow extends Task {
private final UserInteractionLock forceLockDecisionLock;
private final Lazy lockForcedScene;
private final Lazy lockFailedScene;
+ private final ErrorComponent.Builder errorComponent;
@Inject
- public LockWorkflow(@LockWindow Stage lockWindow, @LockWindow Vault vault, UserInteractionLock forceLockDecisionLock, @FxmlScene(FxmlFile.LOCK_FORCED) Lazy lockForcedScene, @FxmlScene(FxmlFile.LOCK_FAILED) Lazy lockFailedScene) {
+ public LockWorkflow(@LockWindow Stage lockWindow, @LockWindow Vault vault, UserInteractionLock forceLockDecisionLock, @FxmlScene(FxmlFile.LOCK_FORCED) Lazy lockForcedScene, @FxmlScene(FxmlFile.LOCK_FAILED) Lazy lockFailedScene, ErrorComponent.Builder errorComponent) {
this.lockWindow = lockWindow;
this.vault = vault;
this.forceLockDecisionLock = forceLockDecisionLock;
this.lockForcedScene = lockForcedScene;
this.lockFailedScene = lockFailedScene;
+ this.errorComponent = errorComponent;
}
@Override
- protected Void call() throws Volume.VolumeException, InterruptedException {
+ protected Void call() throws Volume.VolumeException, InterruptedException, LockNotCompletedException {
try {
vault.lock(false);
- } catch (Volume.VolumeException e) {
+ } catch (Volume.VolumeException | LockNotCompletedException e) {
LOG.debug("Regular lock of {} failed.", vault.getDisplayName(), e);
var decision = askUserForAction();
switch (decision) {
@@ -77,29 +81,29 @@ public class LockWorkflow extends Task {
return forceLockDecisionLock.awaitInteraction();
}
- @Override
- protected void scheduled() {
- vault.setState(VaultState.PROCESSING);
- }
-
@Override
protected void succeeded() {
LOG.info("Lock of {} succeeded.", vault.getDisplayName());
- vault.setState(VaultState.LOCKED);
+ vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
}
@Override
protected void failed() {
- LOG.warn("Failed to lock {}.", vault.getDisplayName());
- vault.setState(VaultState.UNLOCKED);
- lockWindow.setScene(lockFailedScene.get());
- lockWindow.show();
+ final var throwable = super.getException();
+ LOG.warn("Lock of {} failed.", vault.getDisplayName(), throwable);
+ vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.UNLOCKED);
+ if (throwable instanceof Volume.VolumeException) {
+ lockWindow.setScene(lockFailedScene.get());
+ lockWindow.show();
+ } else {
+ errorComponent.cause(throwable).window(lockWindow).build().showErrorScene();
+ }
}
@Override
protected void cancelled() {
LOG.debug("Lock of {} canceled.", vault.getDisplayName());
- vault.setState(VaultState.UNLOCKED);
+ vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.UNLOCKED);
}
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java
index 5a5bb59a6..ea9311c88 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/MainWindowTitleController.java
@@ -95,7 +95,7 @@ public class MainWindowTitleController implements FxController {
@FXML
public void showDonationKeyPreferences() {
- application.showPreferencesWindow(SelectedPreferencesTab.DONATION_KEY);
+ application.showPreferencesWindow(SelectedPreferencesTab.CONTRIBUTE);
}
/* Getter/Setter */
diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java
index 40df6b7c5..7b5c4c7c9 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultDetailController.java
@@ -32,7 +32,8 @@ public class VaultDetailController implements FxController {
this.anyVaultSelected = vault.isNotNull();
}
- private FontAwesome5Icon getGlyphForVaultState(VaultState state) {
+ // TODO deduplicate w/ VaultListCellController
+ private FontAwesome5Icon getGlyphForVaultState(VaultState.Value state) {
if (state != null) {
return switch (state) {
case LOCKED -> FontAwesome5Icon.LOCK;
diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListCellController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListCellController.java
index abc287d6e..f18369e7a 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListCellController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListCellController.java
@@ -24,7 +24,8 @@ public class VaultListCellController implements FxController {
.map(this::getGlyphForVaultState);
}
- private FontAwesome5Icon getGlyphForVaultState(VaultState state) {
+ // TODO deduplicate w/ VaultDetailController
+ private FontAwesome5Icon getGlyphForVaultState(VaultState.Value state) {
if (state != null) {
return switch (state) {
case LOCKED -> FontAwesome5Icon.LOCK;
diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListContextMenuController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListContextMenuController.java
index b0866d80f..145618fa8 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListContextMenuController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListContextMenuController.java
@@ -14,19 +14,13 @@ import org.cryptomator.ui.vaultoptions.VaultOptionsComponent;
import javax.inject.Inject;
import javafx.beans.binding.Binding;
-import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.fxml.FXML;
import javafx.stage.Stage;
-import java.util.Arrays;
import java.util.EnumSet;
import java.util.Optional;
-import static org.cryptomator.common.vaults.VaultState.ERROR;
-import static org.cryptomator.common.vaults.VaultState.LOCKED;
-import static org.cryptomator.common.vaults.VaultState.MISSING;
-import static org.cryptomator.common.vaults.VaultState.NEEDS_MIGRATION;
-import static org.cryptomator.common.vaults.VaultState.UNLOCKED;
+import static org.cryptomator.common.vaults.VaultState.Value.*;
@MainWindowScoped
public class VaultListContextMenuController implements FxController {
@@ -37,7 +31,7 @@ public class VaultListContextMenuController implements FxController {
private final KeychainManager keychain;
private final RemoveVaultComponent.Builder removeVault;
private final VaultOptionsComponent.Builder vaultOptionsWindow;
- private final OptionalBinding selectedVaultState;
+ private final OptionalBinding selectedVaultState;
private final Binding selectedVaultPassphraseStored;
private final Binding selectedVaultRemovable;
private final Binding selectedVaultUnlockable;
@@ -57,7 +51,6 @@ public class VaultListContextMenuController implements FxController {
this.selectedVaultRemovable = selectedVaultState.map(EnumSet.of(LOCKED, MISSING, ERROR, NEEDS_MIGRATION)::contains).orElse(false);
this.selectedVaultUnlockable = selectedVaultState.map(LOCKED::equals).orElse(false);
this.selectedVaultLockable = selectedVaultState.map(UNLOCKED::equals).orElse(false);
-
}
private boolean isPasswordStored(Vault vault) {
diff --git a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java
index 04ea9f152..dbe858a55 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/mainwindow/VaultListController.java
@@ -1,9 +1,11 @@
package org.cryptomator.ui.mainwindow;
+import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.common.vaults.Vault;
import org.cryptomator.common.vaults.VaultListManager;
import org.cryptomator.ui.addvaultwizard.AddVaultWizardComponent;
import org.cryptomator.ui.common.FxController;
+import org.cryptomator.ui.removevault.RemoveVaultComponent;
import javax.inject.Inject;
import javafx.beans.binding.Bindings;
@@ -15,26 +17,39 @@ import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.ListView;
import javafx.scene.input.ContextMenuEvent;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
+import javafx.stage.Stage;
+import java.util.EnumSet;
+
+import static org.cryptomator.common.vaults.VaultState.Value.ERROR;
+import static org.cryptomator.common.vaults.VaultState.Value.LOCKED;
+import static org.cryptomator.common.vaults.VaultState.Value.MISSING;
+import static org.cryptomator.common.vaults.VaultState.Value.NEEDS_MIGRATION;
@MainWindowScoped
public class VaultListController implements FxController {
+ private final Stage mainWindow;
private final ObservableList vaults;
private final ObjectProperty selectedVault;
private final VaultListCellFactory cellFactory;
private final AddVaultWizardComponent.Builder addVaultWizard;
private final BooleanBinding emptyVaultList;
+ private final RemoveVaultComponent.Builder removeVaultDialogue;
public ListView vaultList;
@Inject
- VaultListController(ObservableList vaults, ObjectProperty selectedVault, VaultListCellFactory cellFactory, AddVaultWizardComponent.Builder addVaultWizard) {
+ VaultListController(@MainWindow Stage mainWindow, ObservableList vaults, ObjectProperty selectedVault, VaultListCellFactory cellFactory, AddVaultWizardComponent.Builder addVaultWizard, RemoveVaultComponent.Builder removeVaultDialogue) {
+ this.mainWindow = mainWindow;
this.vaults = vaults;
this.selectedVault = selectedVault;
this.cellFactory = cellFactory;
this.addVaultWizard = addVaultWizard;
+ this.removeVaultDialogue = removeVaultDialogue;
this.emptyVaultList = Bindings.isEmpty(vaults);
@@ -59,6 +74,28 @@ public class VaultListController implements FxController {
request.consume();
}
});
+ vaultList.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
+ if (keyEvent.getCode() == KeyCode.DELETE) {
+ pressedShortcutToRemoveVault();
+ keyEvent.consume();
+ }
+ });
+ if (SystemUtils.IS_OS_MAC) {
+ vaultList.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> {
+ if (keyEvent.getCode() == KeyCode.BACK_SPACE) {
+ pressedShortcutToRemoveVault();
+ keyEvent.consume();
+ }
+ });
+ }
+
+ //register vault selection shortcut to the main window
+ mainWindow.addEventFilter(KeyEvent.KEY_RELEASED, keyEvent -> {
+ if (keyEvent.isShortcutDown() && keyEvent.getCode().isDigitKey()) {
+ vaultList.getSelectionModel().select(Integer.parseInt(keyEvent.getText()) - 1);
+ keyEvent.consume();
+ }
+ });
}
private void deselect(MouseEvent released) {
@@ -80,6 +117,13 @@ public class VaultListController implements FxController {
addVaultWizard.build().showAddVaultWizard();
}
+ private void pressedShortcutToRemoveVault() {
+ final var vault = selectedVault.get();
+ if (vault != null && EnumSet.of(LOCKED, MISSING, ERROR, NEEDS_MIGRATION).contains(vault.getState())) {
+ removeVaultDialogue.vault(vault).build().showRemoveVault();
+ }
+ }
+
// Getter and Setter
public BooleanBinding emptyVaultListProperty() {
diff --git a/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java
index 830e15819..0fc66c992 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/migration/MigrationRunController.java
@@ -26,6 +26,7 @@ import javax.inject.Named;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.ObjectBinding;
+import javafx.beans.binding.ObjectExpression;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty;
@@ -89,7 +90,10 @@ public class MigrationRunController implements FxController {
if (keychain.isSupported()) {
loadStoredPassword();
}
- migrationButtonDisabled.bind(vault.stateProperty().isNotEqualTo(VaultState.NEEDS_MIGRATION).or(passwordField.textProperty().isEmpty()));
+
+ migrationButtonDisabled.bind(ObjectExpression.objectExpression(vault.stateProperty())
+ .isNotEqualTo(VaultState.Value.NEEDS_MIGRATION)
+ .or(passwordField.textProperty().isEmpty()));
}
@FXML
@@ -101,7 +105,7 @@ public class MigrationRunController implements FxController {
public void migrate() {
LOG.info("Migrating vault {}", vault.getPath());
CharSequence password = passwordField.getCharacters();
- vault.setState(VaultState.PROCESSING);
+ vault.stateProperty().transition(VaultState.Value.NEEDS_MIGRATION, VaultState.Value.PROCESSING);
passwordField.setDisable(true);
ScheduledFuture> progressSyncTask = scheduler.scheduleAtFixedRate(() -> {
Platform.runLater(() -> {
@@ -115,10 +119,10 @@ public class MigrationRunController implements FxController {
}).onSuccess(needsAnotherMigration -> {
if (needsAnotherMigration) {
LOG.info("Migration of '{}' succeeded, but another migration is required.", vault.getDisplayName());
- vault.setState(VaultState.NEEDS_MIGRATION);
+ vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
} else {
LOG.info("Migration of '{}' succeeded.", vault.getDisplayName());
- vault.setState(VaultState.LOCKED);
+ vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
passwordField.wipe();
window.setScene(successScene.get());
}
@@ -127,20 +131,20 @@ public class MigrationRunController implements FxController {
passwordField.setDisable(false);
passwordField.selectAll();
passwordField.requestFocus();
- vault.setState(VaultState.NEEDS_MIGRATION);
+ vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
}).onError(FileSystemCapabilityChecker.MissingCapabilityException.class, e -> {
LOG.error("Underlying file system not supported.", e);
- vault.setState(VaultState.NEEDS_MIGRATION);
+ vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
missingCapability.set(e.getMissingCapability());
window.setScene(capabilityErrorScene.get());
}).onError(FileNameTooLongException.class, e -> {
LOG.error("Migration failed because the underlying file system does not support long filenames.", e);
- vault.setState(VaultState.NEEDS_MIGRATION);
+ vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
errorComponent.cause(e).window(window).returnToScene(startScene.get()).build().showErrorScene();
window.setScene(impossibleScene.get());
}).onError(Exception.class, e -> { // including RuntimeExceptions
LOG.error("Migration failed for technical reasons.", e);
- vault.setState(VaultState.NEEDS_MIGRATION);
+ vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.NEEDS_MIGRATION);
errorComponent.cause(e).window(window).returnToScene(startScene.get()).build().showErrorScene();
}).andFinally(() -> {
passwordField.setDisable(false);
diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java
index ea1fbfe3a..64d71a8b7 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/GeneralPreferencesController.java
@@ -157,8 +157,8 @@ public class GeneralPreferencesController implements FxController {
@FXML
- public void showDonationTab() {
- selectedTabProperty.set(SelectedPreferencesTab.DONATION_KEY);
+ public void showContributeTab() {
+ selectedTabProperty.set(SelectedPreferencesTab.CONTRIBUTE);
}
@FXML
diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesController.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesController.java
index 64d5c991b..276794753 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesController.java
@@ -26,7 +26,7 @@ public class PreferencesController implements FxController {
public Tab generalTab;
public Tab volumeTab;
public Tab updatesTab;
- public Tab donationKeyTab;
+ public Tab contributeTab;
public Tab aboutTab;
@Inject
@@ -52,7 +52,7 @@ public class PreferencesController implements FxController {
return switch (selectedTab) {
case UPDATES -> updatesTab;
case VOLUME -> volumeTab;
- case DONATION_KEY -> donationKeyTab;
+ case CONTRIBUTE -> contributeTab;
case GENERAL -> generalTab;
case ABOUT -> aboutTab;
case ANY -> updateAvailable.get() ? updatesTab : generalTab;
diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesModule.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesModule.java
index 51fa6b581..5fee567b7 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesModule.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/PreferencesModule.java
@@ -77,8 +77,8 @@ abstract class PreferencesModule {
@Binds
@IntoMap
- @FxControllerKey(DonationKeyPreferencesController.class)
- abstract FxController bindDonationKeyPreferencesController(DonationKeyPreferencesController controller);
+ @FxControllerKey(SupporterCertificateController.class)
+ abstract FxController bindSupporterCertificatePreferencesController(SupporterCertificateController controller);
@Binds
@IntoMap
diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/SelectedPreferencesTab.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/SelectedPreferencesTab.java
index a76f2ac1c..892d16a8c 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/preferences/SelectedPreferencesTab.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/SelectedPreferencesTab.java
@@ -22,9 +22,9 @@ public enum SelectedPreferencesTab {
UPDATES,
/**
- * Show donation key tab
+ * Show contribute tab
*/
- DONATION_KEY,
+ CONTRIBUTE,
/**
* Show about tab
diff --git a/main/ui/src/main/java/org/cryptomator/ui/preferences/DonationKeyPreferencesController.java b/main/ui/src/main/java/org/cryptomator/ui/preferences/SupporterCertificateController.java
similarity index 68%
rename from main/ui/src/main/java/org/cryptomator/ui/preferences/DonationKeyPreferencesController.java
rename to main/ui/src/main/java/org/cryptomator/ui/preferences/SupporterCertificateController.java
index a4814ec82..02b8bab91 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/preferences/DonationKeyPreferencesController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/preferences/SupporterCertificateController.java
@@ -14,17 +14,17 @@ import javafx.scene.control.TextArea;
import javafx.scene.control.TextFormatter;
@PreferencesScoped
-public class DonationKeyPreferencesController implements FxController {
+public class SupporterCertificateController implements FxController {
- private static final String DONATION_URI = "https://store.cryptomator.org/desktop";
+ private static final String SUPPORTER_URI = "https://store.cryptomator.org/desktop";
private final Application application;
private final LicenseHolder licenseHolder;
private final Settings settings;
- public TextArea donationKeyField;
+ public TextArea supporterCertificateField;
@Inject
- DonationKeyPreferencesController(Application application, LicenseHolder licenseHolder, Settings settings) {
+ SupporterCertificateController(Application application, LicenseHolder licenseHolder, Settings settings) {
this.application = application;
this.licenseHolder = licenseHolder;
this.settings = settings;
@@ -32,9 +32,9 @@ public class DonationKeyPreferencesController implements FxController {
@FXML
public void initialize() {
- donationKeyField.setText(licenseHolder.getLicenseKey().orElse(null));
- donationKeyField.textProperty().addListener(this::registrationKeyChanged);
- donationKeyField.setTextFormatter(new TextFormatter<>(this::checkVaultNameLength));
+ supporterCertificateField.setText(licenseHolder.getLicenseKey().orElse(null));
+ supporterCertificateField.textProperty().addListener(this::registrationKeyChanged);
+ supporterCertificateField.setTextFormatter(new TextFormatter<>(this::checkVaultNameLength));
}
private TextFormatter.Change checkVaultNameLength(TextFormatter.Change change) {
@@ -53,8 +53,8 @@ public class DonationKeyPreferencesController implements FxController {
}
@FXML
- public void getDonationKey() {
- application.getHostServices().showDocument(DONATION_URI);
+ public void getSupporterCertificate() {
+ application.getHostServices().showDocument(SUPPORTER_URI);
}
public LicenseHolder getLicenseHolder() {
diff --git a/main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsModule.java b/main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsModule.java
index 803de314c..6f195274c 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsModule.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/stats/VaultStatisticsModule.java
@@ -43,8 +43,8 @@ abstract class VaultStatisticsModule {
var weakStage = new WeakReference<>(stage);
vault.stateProperty().addListener(new ChangeListener<>() {
@Override
- public void changed(ObservableValue extends VaultState> observable, VaultState oldValue, VaultState newValue) {
- if (newValue != VaultState.UNLOCKED) {
+ public void changed(ObservableValue extends VaultState.Value> observable, VaultState.Value oldValue, VaultState.Value newValue) {
+ if (newValue != VaultState.Value.UNLOCKED) {
Stage stage = weakStage.get();
if (stage != null) {
stage.hide();
diff --git a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java
index 96529c5fb..4ce3808c4 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/traymenu/TrayMenuController.java
@@ -44,6 +44,9 @@ class TrayMenuController {
public void initTrayMenu() {
vaults.addListener(this::vaultListChanged);
+ vaults.forEach(v -> {
+ v.displayNameProperty().addListener(this::vaultListChanged);
+ });
rebuildMenu();
}
diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java
index c1b5fbd45..2815ad3c4 100644
--- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java
+++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockWorkflow.java
@@ -73,22 +73,15 @@ public class UnlockWorkflow extends Task {
this.successScene = successScene;
this.invalidMountPointScene = invalidMountPointScene;
this.errorComponent = errorComponent;
-
- setOnFailed(event -> {
- Throwable throwable = event.getSource().getException();
- if (throwable instanceof InvalidMountPointException e) {
- handleInvalidMountPoint(e);
- } else {
- handleGenericError(throwable);
- }
- });
}
@Override
protected Boolean call() throws InterruptedException, IOException, VolumeException, InvalidMountPointException {
try {
if (attemptUnlock()) {
- handleSuccess();
+ if (savePassword.get()) {
+ savePasswordToSystemkeychain(); //savePassword will be wiped on method return, so it must be set here
+ }
return true;
} else {
cancel(false); // set Tasks state to cancelled
@@ -131,24 +124,6 @@ public class UnlockWorkflow extends Task {
return passwordEntryLock.awaitInteraction();
}
- private void handleSuccess() {
- LOG.info("Unlock of '{}' succeeded.", vault.getDisplayName());
- if (savePassword.get()) {
- savePasswordToSystemkeychain();
- }
- switch (vault.getVaultSettings().actionAfterUnlock().get()) {
- case ASK -> Platform.runLater(() -> {
- window.setScene(successScene.get());
- window.show();
- });
- case REVEAL -> {
- Platform.runLater(window::close);
- vaultService.reveal(vault);
- }
- case IGNORE -> Platform.runLater(window::close);
- }
- }
-
private void savePasswordToSystemkeychain() {
if (keychain.isSupported()) {
try {
@@ -173,15 +148,12 @@ public class UnlockWorkflow extends Task {
LOG.error("Unlock failed. Mountpoint doesn't exist (needs to be a folder): {}", cause.getMessage());
}
showInvalidMountPointScene();
- return;
} else if (cause instanceof FileAlreadyExistsException) {
LOG.error("Unlock failed. Mountpoint already exists: {}", cause.getMessage());
showInvalidMountPointScene();
- return;
} else if (cause instanceof DirectoryNotEmptyException) {
LOG.error("Unlock failed. Mountpoint not an empty directory: {}", cause.getMessage());
showInvalidMountPointScene();
- return;
} else {
handleGenericError(impExc);
}
@@ -196,7 +168,7 @@ public class UnlockWorkflow extends Task {
private void handleGenericError(Throwable e) {
LOG.error("Unlock failed for technical reasons.", e);
- errorComponent.cause(e).window(window).returnToScene(window.getScene()).build().showErrorScene();
+ errorComponent.cause(e).window(window).build().showErrorScene();
}
private void wipePassword(char[] pw) {
@@ -205,24 +177,41 @@ public class UnlockWorkflow extends Task {
}
}
- @Override
- protected void scheduled() {
- vault.setState(VaultState.PROCESSING);
- }
-
@Override
protected void succeeded() {
- vault.setState(VaultState.UNLOCKED);
+ LOG.info("Unlock of '{}' succeeded.", vault.getDisplayName());
+
+ switch (vault.getVaultSettings().actionAfterUnlock().get()) {
+ case ASK -> Platform.runLater(() -> {
+ window.setScene(successScene.get());
+ window.show();
+ });
+ case REVEAL -> {
+ Platform.runLater(window::close);
+ vaultService.reveal(vault);
+ }
+ case IGNORE -> Platform.runLater(window::close);
+ }
+
+ vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.UNLOCKED);
}
@Override
protected void failed() {
- vault.setState(VaultState.LOCKED);
+ LOG.info("Unlock of '{}' failed.", vault.getDisplayName());
+ Throwable throwable = super.getException();
+ if (throwable instanceof InvalidMountPointException e) {
+ handleInvalidMountPoint(e);
+ } else {
+ handleGenericError(throwable);
+ }
+ vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
}
@Override
protected void cancelled() {
- vault.setState(VaultState.LOCKED);
+ LOG.debug("Unlock of '{}' canceled.", vault.getDisplayName());
+ vault.stateProperty().transition(VaultState.Value.PROCESSING, VaultState.Value.LOCKED);
}
}
diff --git a/main/ui/src/main/resources/fxml/addvault_new_location.fxml b/main/ui/src/main/resources/fxml/addvault_new_location.fxml
index 66b34ff95..3c25ba569 100644
--- a/main/ui/src/main/resources/fxml/addvault_new_location.fxml
+++ b/main/ui/src/main/resources/fxml/addvault_new_location.fxml
@@ -20,6 +20,8 @@
alignment="CENTER_LEFT">
+
+
@@ -33,6 +35,8 @@
+
+
@@ -33,8 +33,8 @@
-
-
+
+
@@ -43,6 +43,6 @@
-
+
diff --git a/main/ui/src/main/resources/fxml/preferences_general.fxml b/main/ui/src/main/resources/fxml/preferences_general.fxml
index c167e392c..ea087b9e3 100644
--- a/main/ui/src/main/resources/fxml/preferences_general.fxml
+++ b/main/ui/src/main/resources/fxml/preferences_general.fxml
@@ -23,7 +23,7 @@
-
+
diff --git a/main/ui/src/main/resources/i18n/strings.properties b/main/ui/src/main/resources/i18n/strings.properties
index e02f04bb1..824f65811 100644
--- a/main/ui/src/main/resources/i18n/strings.properties
+++ b/main/ui/src/main/resources/i18n/strings.properties
@@ -45,8 +45,10 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Custom Location
addvaultwizard.new.directoryPickerButton=Choose…
addvaultwizard.new.directoryPickerTitle=Select Directory
-addvaultwizard.new.fileAlreadyExists=Vault can not be created at this path because some object already exists.
-addvaultwizard.new.locationDoesNotExist=Vault can not be created at this path because at least one path component does not exist.
+addvaultwizard.new.fileAlreadyExists=A file or directory with the vault name already exists
+addvaultwizard.new.locationDoesNotExist=A directory in the specified path does not exist or cannot be accessed
+addvaultwizard.new.locationIsNotWritable=No write access at the specified path
+addvaultwizard.new.locationIsOk=Suitable location for your vault
addvaultwizard.new.invalidName=Invalid vault name. Please consider a regular directory name.
### Password
addvaultwizard.new.createVaultBtn=Create Vault
@@ -175,11 +177,14 @@ preferences.updates.currentVersion=Current Version: %s
preferences.updates.autoUpdateCheck=Check for updates automatically
preferences.updates.checkNowBtn=Check Now
preferences.updates.updateAvailable=Update to version %s available.
-## Donation Key
-preferences.donationKey=Donation
-preferences.donationKey.registeredFor=Registered for %s
-preferences.donationKey.noDonationKey=No valid donation key found. It's like a license key but for awesome people using free software. ;-)
-preferences.donationKey.getDonationKey=Get a donation key
+## Contribution
+preferences.contribute=Support Us
+preferences.contribute.registeredFor=Supporter certificate registered for %s
+preferences.contribute.noCertificate=Support Cryptomator and receive a supporter certificate. It's like a license key but for awesome people using free software. ;-)
+preferences.contribute.getCertificate=Don't have one already? Learn how you can obtain it.
+preferences.contribute.promptText=Paste supporter certificate code here
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=About
diff --git a/main/ui/src/main/resources/i18n/strings_ar.properties b/main/ui/src/main/resources/i18n/strings_ar.properties
index e9292b402..38f4ad9b5 100644
--- a/main/ui/src/main/resources/i18n/strings_ar.properties
+++ b/main/ui/src/main/resources/i18n/strings_ar.properties
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=موقع مخصص
addvaultwizard.new.directoryPickerButton=اختر…
addvaultwizard.new.directoryPickerTitle=اختر القاموس
-addvaultwizard.new.fileAlreadyExists=لا يمكن إنشاء مخزن على هذا المسار لأن بعض الملفات موجودة مسبقا.
-addvaultwizard.new.locationDoesNotExist=لا يمكن إنشاء المخزن على هذا المسار لأن جزء من هذا المسار غير موجود.
addvaultwizard.new.invalidName=اسم المخزن غير صالح. يرجى النظر في اسم الدليل المعتاد.
### Password
addvaultwizard.new.createVaultBtn=انشئ حافظة
@@ -167,11 +165,9 @@ preferences.updates.currentVersion=الإصدار الحالي: %s
preferences.updates.autoUpdateCheck=تحقق من التحديثات اوتوماتيكيا
preferences.updates.checkNowBtn=تحقق الان
preferences.updates.updateAvailable=التحديث إلى الإصدار %s متاح.
-## Donation Key
-preferences.donationKey=التبرعات
-preferences.donationKey.registeredFor=مسجل لـ %s
-preferences.donationKey.noDonationKey=لم يتم العثور على مفتاح تبرع صحيح. إنه مثل مفتاح الترخيص إلا أنه للأشخاص الرائعين الذين يستخدمون البرمجيات المجانية؛-)
-preferences.donationKey.getDonationKey=الحصول على مفتاح تبرع
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=حول البرنامج
diff --git a/main/ui/src/main/resources/i18n/strings_bs.properties b/main/ui/src/main/resources/i18n/strings_bs.properties
index a427b7f53..8b9e07e3f 100644
--- a/main/ui/src/main/resources/i18n/strings_bs.properties
+++ b/main/ui/src/main/resources/i18n/strings_bs.properties
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Prilagođena lokacija
addvaultwizard.new.directoryPickerButton=Odaberi…
addvaultwizard.new.directoryPickerTitle=Izaberi folder
-addvaultwizard.new.fileAlreadyExists=Sef ne može biti kreiran na odabranoj lokaciji jer neki objekti tu već postoje.
-addvaultwizard.new.locationDoesNotExist=Sef ne može biti kreiran na odabranoj lokaciji jer najmanje jedna komponenta lokacije ne postoji.
addvaultwizard.new.invalidName=Naziv sefa neispravan. Molimo razmislite o drugom nazivu.
### Password
addvaultwizard.new.createVaultBtn=Kreiraj novi sef
@@ -95,8 +93,10 @@ forgetPassword.confirmBtn=Zaboravili ste šifru
# Unlock
unlock.title=Otključaj sef
unlock.passwordPrompt=Unesite lozinku za "%s":
+unlock.savePassword=Zapamti šifru
unlock.unlockBtn=Otključaj
## Success
+unlock.success.message=Uspješno ste otključali "%s"! Vaš sef je sada dostupan putem svog virtualnog diska.
unlock.success.rememberChoice=Zapamtite izbor, ne pokazujte ovo ponovo
unlock.success.revealBtn=Otkrij pogon
## Failure
@@ -172,11 +172,9 @@ preferences.updates.currentVersion=Trenutna verzija: %s
preferences.updates.autoUpdateCheck=Automatski provjeri ima li ažuriranja
preferences.updates.checkNowBtn=Provjeri sada
preferences.updates.updateAvailable=Dostupno ažuriranje na verziju %s.
-## Donation Key
-preferences.donationKey=Donacija
-preferences.donationKey.registeredFor=Registrovan za %s
-preferences.donationKey.noDonationKey=Nije pronađen važeći ključ za donaciju. To je poput licence, ali za sjajne ljude koji koriste besplatni softver. ;-)
-preferences.donationKey.getDonationKey=Nabavite ključ za donaciju
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=O programu
@@ -221,9 +219,11 @@ main.dropZone.dropVault=Dodajte ovaj sef
main.dropZone.unknownDragboardContent=Ako želite dodati sef, povucite ga u ovaj prozor
## Vault List
main.vaultlist.emptyList.onboardingInstruction=Kliknite ovdje da dodate sef
+main.vaultlist.contextMenu.remove=Ukloni…
main.vaultlist.contextMenu.lock=Zaključaj
main.vaultlist.contextMenu.unlock=Otključaj…
main.vaultlist.contextMenu.unlockNow=Otključaj sada
+main.vaultlist.contextMenu.vaultoptions=Pokaži opcije sefa
main.vaultlist.contextMenu.reveal=Otkrij pogon
main.vaultlist.addVaultBtn=Dodaj sef
## Vault Detail
diff --git a/main/ui/src/main/resources/i18n/strings_ca.properties b/main/ui/src/main/resources/i18n/strings_ca.properties
index be814a57b..f46b4a016 100644
--- a/main/ui/src/main/resources/i18n/strings_ca.properties
+++ b/main/ui/src/main/resources/i18n/strings_ca.properties
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Ubicació personalitzada
addvaultwizard.new.directoryPickerButton=Trieu…
addvaultwizard.new.directoryPickerTitle=Seleccioneu el directori
-addvaultwizard.new.fileAlreadyExists=No es pot crear una caixa forta en aquest camí perquè ja hi ha algun objecte.
-addvaultwizard.new.locationDoesNotExist=No ha estat possible crear la caixa forta en aquest camí perquè, si més no, un component no existeix.
addvaultwizard.new.invalidName=El nom de la caixa forta no és vàlid. Si us plau, escribiu un mom de directori amb caràcters estàndard.
### Password
addvaultwizard.new.createVaultBtn=Crea la caixa forta
@@ -98,6 +96,7 @@ unlock.passwordPrompt=Introduïu la contrasenya de "%s":
unlock.savePassword=Recorda la contrasenya
unlock.unlockBtn=Desbloqueja
## Success
+unlock.success.message=S'ha desblocat %s correctament! Podeu accedir a la vostra caixa forta a través de la unitat virtual.
unlock.success.rememberChoice=Recorda l'elecció. No ho tornis a mostrar.
unlock.success.revealBtn=Mostra la unitat
## Failure
@@ -173,11 +172,14 @@ preferences.updates.currentVersion=Versió actual: %s
preferences.updates.autoUpdateCheck=Comprova automàticament si hi ha actualitzacions
preferences.updates.checkNowBtn=Comprova-ho ara
preferences.updates.updateAvailable=L'actualització a la versió %s es troba disponible.
-## Donation Key
-preferences.donationKey=Donacions
-preferences.donationKey.registeredFor=Registrat per %s
-preferences.donationKey.noDonationKey=No s'ha trobar una clau de donació vàlida. És sembla a una clau de llicència però per gent meravellosa qui utilitza programari lliure. ;-)
-preferences.donationKey.getDonationKey=Obtingau una clau de donació
+## Contribution
+preferences.contribute=Doneu-nos suport
+preferences.contribute.registeredFor=Certificat de col·laborador registrat per a %s
+preferences.contribute.noCertificate=Doneu suport a Cryptomator i rebeu un certificat de col·laborador. És com una clau de llicència però per a gent meravellosa que fa servir programari lliure. ;-)
+preferences.contribute.getCertificate=Encara no en teniu cap? Sapigueu com aconseguir-ne un.
+preferences.contribute.promptText=Enganxeu aquí el certificat de col·laborador
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=Quant a
@@ -222,9 +224,11 @@ main.dropZone.dropVault=Afegeix aquesta caixa forta
main.dropZone.unknownDragboardContent=Si voleu afegir una caixa forta, arrossegueu-la a aquesta finestra
## Vault List
main.vaultlist.emptyList.onboardingInstruction=Feu clic aquí per afegir una caixa forta
+main.vaultlist.contextMenu.remove=Elimina…
main.vaultlist.contextMenu.lock=Bloqueja
main.vaultlist.contextMenu.unlock=Desbloca…
main.vaultlist.contextMenu.unlockNow=Desbloqueja ara
+main.vaultlist.contextMenu.vaultoptions=Opcions de la caixa forta
main.vaultlist.contextMenu.reveal=Mostra la unitat
main.vaultlist.addVaultBtn=Afegir una caixa forta
## Vault Detail
diff --git a/main/ui/src/main/resources/i18n/strings_cs.properties b/main/ui/src/main/resources/i18n/strings_cs.properties
index 363ac857d..8d5e6c395 100644
--- a/main/ui/src/main/resources/i18n/strings_cs.properties
+++ b/main/ui/src/main/resources/i18n/strings_cs.properties
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=...
addvaultwizard.new.directoryPickerLabel=Vlastní umístění
addvaultwizard.new.directoryPickerButton=Vybrat...
addvaultwizard.new.directoryPickerTitle=Vyberte adresář
-addvaultwizard.new.fileAlreadyExists=Trezor nemůže být vytvořen na tomto umístění, protože stejný objekt již existuje.
-addvaultwizard.new.locationDoesNotExist=Trezor nemůže být vytvořen v tomto umístění, protože přinejmenším jedna položka z daného umístění neexistuje.
+addvaultwizard.new.fileAlreadyExists=Soubor nebo složka s tímto názvem trezoru již existuje
+addvaultwizard.new.locationDoesNotExist=Složka v zadané cestě neexistuje nebo není přístupná
+addvaultwizard.new.locationIsNotWritable=Není možné zapisovat na zadané cestě
+addvaultwizard.new.locationIsOk=Vhodné umístění pro váš trezor
addvaultwizard.new.invalidName=Neplatné jméno trezoru. Prosím změňte jméno adresáře.
### Password
addvaultwizard.new.createVaultBtn=Vytvořit trezor
@@ -98,6 +100,7 @@ unlock.passwordPrompt=Zadejte heslo pro "%s":
unlock.savePassword=Zapamatovat heslo
unlock.unlockBtn=Odemknout
## Success
+unlock.success.message=Trezor "%s" byl úspěšně odemčen a nyní je dostupný jako virtuální jednotka.
unlock.success.rememberChoice=Pamatovat si volbu, nezobrazovat to znovu
unlock.success.revealBtn=Zobrazit jednotku
## Failure
@@ -109,8 +112,11 @@ unlock.error.invalidMountPoint.existing=Připojovací bod %s již existuje nebo
# Lock
## Force
lock.forced.heading=Běžné uzamčení selhalo
+lock.forced.message=Uzamčení "%s" bylo zablokováno nevyřízenými operacemi nebo otevřenými soubory. Můžete vynutit uzamčení tohoto trezoru, ale přerušení I/O může mít za následek ztrátu neuložených dat.
lock.forced.confirmBtn=Přesto uzamknout
## Failure
+lock.fail.heading=Uzamčení trezoru selhalo.
+lock.fail.message=Trezor "%s" nelze uzamknout. Ujistěte se, že je neuložená práce uložena jinde a že jsou dokončeny důležité operace čtení/zápis. Za účelem uzavření trezoru ukončte proces Cryptomatoru.
# Migration
migration.title=Upgrade trezoru
@@ -170,11 +176,14 @@ preferences.updates.currentVersion=Aktuální verze: %s
preferences.updates.autoUpdateCheck=Automaticky kontrolovat aktualizace
preferences.updates.checkNowBtn=Zkontrolovat nyní
preferences.updates.updateAvailable=Dostupná nová verze %s.
-## Donation Key
-preferences.donationKey=Příspěvek
-preferences.donationKey.registeredFor=Registrováno na: %s
-preferences.donationKey.noDonationKey=Nebyl nalezen žádný platný darovací klíč. Je to jako licenční klíč, ale pro úžasné lidi využívající software zadarmo. ;-)
-preferences.donationKey.getDonationKey=Získat darovací klíč
+## Contribution
+preferences.contribute=Podpořte nás
+preferences.contribute.registeredFor=Certifikát podporovatele byl registrován pro %s
+preferences.contribute.noCertificate=Podpořte Cryptomator a získejte certifikát podporovatele. Je to jako licenční klíč, ale pro skvělé lidi, kteří používají svobodný software. ;-)
+preferences.contribute.getCertificate=Ještě žádný nemáte? Naučte se, jak ho můžete získat.
+preferences.contribute.promptText=Sem vložte kód certifikátu podporovatele
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=O aplikaci
@@ -223,6 +232,7 @@ main.vaultlist.contextMenu.remove=Odstranit…
main.vaultlist.contextMenu.lock=Zamknout
main.vaultlist.contextMenu.unlock=Odemknout…
main.vaultlist.contextMenu.unlockNow=Odemknout nyní
+main.vaultlist.contextMenu.vaultoptions=Zobrazit možnosti trezoru
main.vaultlist.contextMenu.reveal=Zobrazit jednotku
main.vaultlist.addVaultBtn=Přidat trezor
## Vault Detail
diff --git a/main/ui/src/main/resources/i18n/strings_de.properties b/main/ui/src/main/resources/i18n/strings_de.properties
index e500aab6c..a7339f9c6 100644
--- a/main/ui/src/main/resources/i18n/strings_de.properties
+++ b/main/ui/src/main/resources/i18n/strings_de.properties
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Eigener Ort
addvaultwizard.new.directoryPickerButton=Durchsuchen …
addvaultwizard.new.directoryPickerTitle=Verzeichnis auswählen
-addvaultwizard.new.fileAlreadyExists=Der Tresor konnte nicht erstellt werden, da am Speicherort so eine Datei bereits existiert.
-addvaultwizard.new.locationDoesNotExist=Der Tresor kann nicht erstellt werden, da mindestens ein Speicherort nicht existiert.
+addvaultwizard.new.fileAlreadyExists=Eine Datei oder ein Ordner mit diesem Namen ist bereits vorhanden
+addvaultwizard.new.locationDoesNotExist=Der Ordner im angegebenen Pfad existiert nicht oder kann nicht geöffnet werden
+addvaultwizard.new.locationIsNotWritable=Kein Schreibzugriff auf den angegebenen Pfad
+addvaultwizard.new.locationIsOk=Geeigneter Ort für deinen Tresor
addvaultwizard.new.invalidName=Ungültiger Tresorname. Bitte wähle einen regulären Namen, mit dem auch Verzeichnisse erstellt werden können.
### Password
addvaultwizard.new.createVaultBtn=Tresor erstellen
@@ -174,11 +176,14 @@ preferences.updates.currentVersion=Aktuelle Version: %s
preferences.updates.autoUpdateCheck=Automatisch nach Updates suchen
preferences.updates.checkNowBtn=Jetzt suchen
preferences.updates.updateAvailable=Update auf Version %s verfügbar.
-## Donation Key
-preferences.donationKey=Spende
-preferences.donationKey.registeredFor=Registriert für %s
-preferences.donationKey.noDonationKey=Kein gültiger Spendenschlüssel gefunden. Er ist wie ein Lizenzschlüssel, aber für tolle Leute, die freie Software verwenden. ;-)
-preferences.donationKey.getDonationKey=Hol dir einen Spendenschlüssel
+## Contribution
+preferences.contribute=Unterstütze uns
+preferences.contribute.registeredFor=Supporter-Zertifikat registriert für %s
+preferences.contribute.noCertificate=Unterstütze Cryptomator und erhalte ein Supporter-Zertifikat. Es ist wie ein Lizenzschlüssel, aber für großartige Menschen, die freie Software verwenden. ;-)
+preferences.contribute.getCertificate=Du hast noch keines? Erfahre, wie du es erhalten kannst.
+preferences.contribute.promptText=Code des Supporter-Zertifikats hier einfügen
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=Über
diff --git a/main/ui/src/main/resources/i18n/strings_el.properties b/main/ui/src/main/resources/i18n/strings_el.properties
index de5d4b147..bf8cccf4c 100644
--- a/main/ui/src/main/resources/i18n/strings_el.properties
+++ b/main/ui/src/main/resources/i18n/strings_el.properties
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Προσαρμοσμένη τοποθεσία
addvaultwizard.new.directoryPickerButton=Επιλογή…
addvaultwizard.new.directoryPickerTitle=Επιλογή φακέλου
-addvaultwizard.new.fileAlreadyExists=Το vault δεν μπορεί να δημιουργηθεί σε αυτό το μονοπάτι καθώς κάποια αντικείμενα υπάρχουν ήδη.
-addvaultwizard.new.locationDoesNotExist=Το vault δεν μπορεί να δημιουργηθεί σε αυτό το μονοπάτι καθώς τουλάχιστον ένα στοιχείο του μονοπατιού δεν υπάρχει.
+addvaultwizard.new.fileAlreadyExists=Ένα αρχείο ή φάκελος με το όνομα του vault υπάρχει ήδη
+addvaultwizard.new.locationDoesNotExist=Στην καθορισμένη διαδρομή δεν υπάρχει ή δεν μπορεί να προσπελαστεί ένας φάκελος
+addvaultwizard.new.locationIsNotWritable=Δεν υπάρχει πρόσβαση εγγραφής στην καθορισμένη διαδρομή
+addvaultwizard.new.locationIsOk=Κατάλληλη τοποθεσία για το vault σας
addvaultwizard.new.invalidName=Λάθος όνομα vault. Παρακαλώ χρησιμοποιείστε ένα κανονικό όνομα φακέλου.
### Password
addvaultwizard.new.createVaultBtn=Δημιουργία Vault
@@ -98,6 +100,7 @@ unlock.passwordPrompt=Εισάγετε τον κωδικό για "%s":
unlock.savePassword=Απομνημόνευση κωδικού πρόσβασης
unlock.unlockBtn=Ξεκλείδωμα
## Success
+unlock.success.message=Ξεκλειδώθηκε "%s" επιτυχώς! Το vault σας είναι διαθέσιμο μέσω του εικονικού δίσκου του.
unlock.success.rememberChoice=Απομνημόνευση επιλογής, μην ρωτήσεις ξανά
unlock.success.revealBtn=Αποκάλυψη εικονικού δίσκου
## Failure
@@ -173,11 +176,14 @@ preferences.updates.currentVersion=Τρέχουσα έκδοση: %s
preferences.updates.autoUpdateCheck=Αυτόματος έλεγχος για ενημερώσεις
preferences.updates.checkNowBtn=Έλεγχος τώρα
preferences.updates.updateAvailable=Η ενημέρωση για την έκδοση %s είναι διαθέσιμη.
-## Donation Key
-preferences.donationKey=Δωρεά
-preferences.donationKey.registeredFor=Καταχωρημένο σε %s
-preferences.donationKey.noDonationKey=Δεν βρέθηκε ενεργό κλειδί δωρεάς. Είναι σαν κλειδί αγοράς αλλά για απίθανους ανθρώπους που χρησιμοποιούν δωρεάν λογισμικό. ;-)
-preferences.donationKey.getDonationKey=Πάρε ένα κλειδί δωρεάς
+## Contribution
+preferences.contribute=Υποστηρίξτε μας
+preferences.contribute.registeredFor=Το πιστοποιητικό υποστήριξης καταχωρήθηκε για %s
+preferences.contribute.noCertificate=Υποστηρίξτε το Cryptomator και λάβετε ένα πιστοποιητικό υποστηρικτή. Είναι σαν κλειδί άδειας χρήσης, αλλά για φοβερά άτομα που χρησιμοποιούν ελεύθερο λογισμικό. ;-)
+preferences.contribute.getCertificate=Δεν έχετε ένα ήδη; Μάθετε πώς μπορείτε να το αποκτήσετε.
+preferences.contribute.promptText=Επικολλήστε τον κωδικό πιστοποιητικού υποστηρικτή εδώ
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=Σχετικά με
@@ -222,9 +228,11 @@ main.dropZone.dropVault=Προσθήκη vault
main.dropZone.unknownDragboardContent=Αν θέλετε να προσθέσετε ένα vault, σύρετε το σε αυτό το παράθυρο
## Vault List
main.vaultlist.emptyList.onboardingInstruction=Κάντε κλικ εδώ για να προσθέσετε ένα vault
+main.vaultlist.contextMenu.remove=Αφαίρεση…
main.vaultlist.contextMenu.lock=Κλείδωμα
main.vaultlist.contextMenu.unlock=Ξεκλείδωμα…
main.vaultlist.contextMenu.unlockNow=Ξεκλείδωμα τώρα
+main.vaultlist.contextMenu.vaultoptions=Εμφάνιση επιλογών Vault
main.vaultlist.contextMenu.reveal=Αποκάλυψη εικονικού δίσκου
main.vaultlist.addVaultBtn=Προσθήκη Vault
## Vault Detail
diff --git a/main/ui/src/main/resources/i18n/strings_es.properties b/main/ui/src/main/resources/i18n/strings_es.properties
index de683e3a8..a3cd6a8e5 100644
--- a/main/ui/src/main/resources/i18n/strings_es.properties
+++ b/main/ui/src/main/resources/i18n/strings_es.properties
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Ubicación personalizada
addvaultwizard.new.directoryPickerButton=Elegir…
addvaultwizard.new.directoryPickerTitle=Seleccionar directorio
-addvaultwizard.new.fileAlreadyExists=La bóveda no puede crearse en esta ruta porque ya existe un objeto.
-addvaultwizard.new.locationDoesNotExist=La bóveda no puede ser creada en esta ruta porque al menos un componente no existe.
+addvaultwizard.new.fileAlreadyExists=Ya existe un archivo o directorio con el nombre de la bóveda
+addvaultwizard.new.locationDoesNotExist=No existe un directorio en la ruta especificada o no se puede acceder
+addvaultwizard.new.locationIsNotWritable=Sin acceso de escritura en la ruta especificada
+addvaultwizard.new.locationIsOk=Ubicación adecuada para la bóveda
addvaultwizard.new.invalidName=Nombre de bóveda inválido. Considere un nombre de directorio regular.
### Password
addvaultwizard.new.createVaultBtn=Crear bóveda
@@ -174,11 +176,9 @@ preferences.updates.currentVersion=Versión actual: %s
preferences.updates.autoUpdateCheck=Buscar actualizaciones automáticamente
preferences.updates.checkNowBtn=Buscar ahora
preferences.updates.updateAvailable=Actualización a la versión %s disponible.
-## Donation Key
-preferences.donationKey=Donación
-preferences.donationKey.registeredFor=Registrado para %s
-preferences.donationKey.noDonationKey=No se encontró una clave de donación válida. Es como una clave de licencia pero para personas geniales que usan software libre.
-preferences.donationKey.getDonationKey=Obtener clave de donación
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=Acerca de
diff --git a/main/ui/src/main/resources/i18n/strings_fr.properties b/main/ui/src/main/resources/i18n/strings_fr.properties
index 27c9951e2..9b67cf9c9 100644
--- a/main/ui/src/main/resources/i18n/strings_fr.properties
+++ b/main/ui/src/main/resources/i18n/strings_fr.properties
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Emplacement personnalisé
addvaultwizard.new.directoryPickerButton=Choisir...
addvaultwizard.new.directoryPickerTitle=Choisissez le répertoire
-addvaultwizard.new.fileAlreadyExists=Le coffre ne peut pas être créé sur ce chemin car un objet existe déjà.
-addvaultwizard.new.locationDoesNotExist=Le coffre ne peut pas être créé à cet endroit car au moins un répertoire du chemin n'existe pas.
+addvaultwizard.new.fileAlreadyExists=Un fichier ou un répertoire avec ce nom existe déjà
+addvaultwizard.new.locationDoesNotExist=Un répertoire dans le chemin spécifié n'existe pas ou ne peut pas être accédé
+addvaultwizard.new.locationIsNotWritable=Pas d'accès en écriture au chemin spécifié
+addvaultwizard.new.locationIsOk=Lieu approprié pour votre coffre
addvaultwizard.new.invalidName=Nom de coffre invalide. Préférez un nom de répertoire habituel.
### Password
addvaultwizard.new.createVaultBtn=Créer un coffre
@@ -174,11 +176,14 @@ preferences.updates.currentVersion=Version actuelle : “%s”
preferences.updates.autoUpdateCheck=Vérifier automatiquement l’existence de mise à jour
preferences.updates.checkNowBtn=Vérifier maintenant
preferences.updates.updateAvailable=Mise à jour “%s” disponible.
-## Donation Key
-preferences.donationKey=Faire un Don
-preferences.donationKey.registeredFor=Licence enregistrée à %s
-preferences.donationKey.noDonationKey=Aucune clé de don valide trouvée. C'est comme une clé de licence mais pour des personnes géniales utilisant un logiciel libre ;-)
-preferences.donationKey.getDonationKey=Obtenir une clé de don
+## Contribution
+preferences.contribute=Nous soutenir
+preferences.contribute.registeredFor=Certificat de soutien enregistré pour %s
+preferences.contribute.noCertificate=Soutenez Cryptomator et recevez un certificat de soutien. C'est comme une clé de licence, mais pour des gens géniaux qui utilisent des logiciels libres. ;-)
+preferences.contribute.getCertificate=Vous n'en avez pas encore? Découvrez comment vous pouvez l'obtenir.
+preferences.contribute.promptText=Collez le code du certificat du supporter ici
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=A propos
diff --git a/main/ui/src/main/resources/i18n/strings_hi.properties b/main/ui/src/main/resources/i18n/strings_hi.properties
index 85d139a01..873af0f50 100644
--- a/main/ui/src/main/resources/i18n/strings_hi.properties
+++ b/main/ui/src/main/resources/i18n/strings_hi.properties
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=अपने पसंद की जगह डालें
addvaultwizard.new.directoryPickerButton=चुनें…
addvaultwizard.new.directoryPickerTitle=निर्देशिका चुनें
-addvaultwizard.new.fileAlreadyExists=तिजोरी इस रास्ते पर नहीं बनाई जा सकती क्योंकि कुछ वस्तु पहले से मौजूद है।
-addvaultwizard.new.locationDoesNotExist=तिजोरी इस रास्ते पर नहीं बनाई जा सकती क्योंकि कम से कम एक पथ घटक मौजूद नहीं है।
addvaultwizard.new.invalidName=अमान्य वॉल्ट नाम। कृपया एक नियमित निर्देशिका नाम पर विचार करें।
### Password
addvaultwizard.new.createVaultBtn=वॉल्ट बनाएं
@@ -94,7 +92,9 @@ preferences.title=प्राथमिकताएं
preferences.general=सामान्य
## Volume
## Updates
-## Donation Key
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
# Vault Statistics
diff --git a/main/ui/src/main/resources/i18n/strings_hr.properties b/main/ui/src/main/resources/i18n/strings_hr.properties
index 9f2898f25..8dce8a60c 100644
--- a/main/ui/src/main/resources/i18n/strings_hr.properties
+++ b/main/ui/src/main/resources/i18n/strings_hr.properties
@@ -44,7 +44,9 @@
## General
## Volume
## Updates
-## Donation Key
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
# Vault Statistics
diff --git a/main/ui/src/main/resources/i18n/strings_hu.properties b/main/ui/src/main/resources/i18n/strings_hu.properties
index 6d212b025..5604c015e 100644
--- a/main/ui/src/main/resources/i18n/strings_hu.properties
+++ b/main/ui/src/main/resources/i18n/strings_hu.properties
@@ -43,8 +43,6 @@ addvaultwizard.new.locationLabel=Tárolási hely
addvaultwizard.new.directoryPickerLabel=Egyedi hely
addvaultwizard.new.directoryPickerButton=Választás…
addvaultwizard.new.directoryPickerTitle=Könyvtár kiválasztása
-addvaultwizard.new.fileAlreadyExists=Nem lehet a széfet létrehozni ezen a helyen, mert egy fájl már létezik itt.
-addvaultwizard.new.locationDoesNotExist=Nem lehet a széfet létrehozni ezen a helyen, mert az útvonal legalább egy darabja nem létezik.
addvaultwizard.new.invalidName=Érvénytelen széf elnevezés. Kérjük vegye figyelembe a szabályos könyvtárelnevezésre vonatkozó szabályokat.
### Password
addvaultwizard.new.createVaultBtn=Új széf létrehozása
@@ -158,11 +156,9 @@ preferences.updates.currentVersion=Jelenlegi verzió: %s
preferences.updates.autoUpdateCheck=Frissítések autómatikus keresése
preferences.updates.checkNowBtn=Ellenőrzés most
preferences.updates.updateAvailable=Frissítés a %s verzióra elérhető.
-## Donation Key
-preferences.donationKey=Adomány
-preferences.donationKey.registeredFor=Regisztrálva %s számára
-preferences.donationKey.noDonationKey=Nem található érvényes adománykulcs. Mint egy licenckulcs, csak olyan nagyszerű emberek számára, akik ingyenes szofvereket használnak. ;-)
-preferences.donationKey.getDonationKey=Adománykulcs beszerzése
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=Rólunk
diff --git a/main/ui/src/main/resources/i18n/strings_id.properties b/main/ui/src/main/resources/i18n/strings_id.properties
index 20bc657d7..80a018fa8 100644
--- a/main/ui/src/main/resources/i18n/strings_id.properties
+++ b/main/ui/src/main/resources/i18n/strings_id.properties
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Sesuaikan Lokasi
addvaultwizard.new.directoryPickerButton=Pilih…
addvaultwizard.new.directoryPickerTitle=Pilih Folder
-addvaultwizard.new.fileAlreadyExists=Brankas tidak dibuat disini karena beberapa item telah ada disini.
-addvaultwizard.new.locationDoesNotExist=Brankas tidak bisa dibuat disini karena setidaknya satu jalur komponen tidak ada.
addvaultwizard.new.invalidName=Nama brankas salah. Harap pilih nama folder yang umum.
### Password
addvaultwizard.new.createVaultBtn=Buat Brankas
@@ -97,7 +95,9 @@ preferences.title=Preferensi
## General
## Volume
## Updates
-## Donation Key
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
# Vault Statistics
diff --git a/main/ui/src/main/resources/i18n/strings_it.properties b/main/ui/src/main/resources/i18n/strings_it.properties
index 3636aa968..1593cee8c 100644
--- a/main/ui/src/main/resources/i18n/strings_it.properties
+++ b/main/ui/src/main/resources/i18n/strings_it.properties
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Posizione personalizzata
addvaultwizard.new.directoryPickerButton=Scegli…
addvaultwizard.new.directoryPickerTitle=Seleziona cartella
-addvaultwizard.new.fileAlreadyExists=La cassaforte non può essere creata in questo percorso perché un oggetto esiste già.
-addvaultwizard.new.locationDoesNotExist=La cassaforte non può essere creata in questo percorso perché almeno un componente di percorso non esiste.
addvaultwizard.new.invalidName=Nome della cassaforte non valido. Si prega di considerare un nome di directory regolare.
### Password
addvaultwizard.new.createVaultBtn=Crea Cassaforte
@@ -98,8 +96,9 @@ unlock.passwordPrompt=Inserisci la password per "%s":
unlock.savePassword=Ricorda la Password
unlock.unlockBtn=Sblocca
## Success
+unlock.success.message="%s" sbloccato con successo! La tua cassaforte è ora accessibile tramite il suo drive virtuale.
unlock.success.rememberChoice=Ricorda la scelta, non mostrare ancora
-unlock.success.revealBtn=Visualizza disco
+unlock.success.revealBtn=Rivela Drive
## Failure
unlock.error.heading=Impossibile sbloccare la cassaforte
### Invalid Mount Point
@@ -172,11 +171,9 @@ preferences.updates.currentVersion=Versione attuale %s
preferences.updates.autoUpdateCheck=Controlla automaticamente la presenza di aggiornamenti
preferences.updates.checkNowBtn=Controlla adesso
preferences.updates.updateAvailable=Disponibile aggiornamento alla versione %s.
-## Donation Key
-preferences.donationKey=Donazione
-preferences.donationKey.registeredFor=Registrato per %s
-preferences.donationKey.noDonationKey=Nessuna chiave di donazione valida. È come una chiave di licenza ma per persone fantastiche che usano software gratuito. ;-)
-preferences.donationKey.getDonationKey=Ottieni una chiave di donazione
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=Informazioni
@@ -220,10 +217,12 @@ main.dropZone.dropVault=Aggiungi questa cassaforte
main.dropZone.unknownDragboardContent=Se vuoi aggiungere una cassaforte, trascinala in questa finestra
## Vault List
main.vaultlist.emptyList.onboardingInstruction=Clicca qui per aggiungere una cassaforte
+main.vaultlist.contextMenu.remove=Rimuovi…
main.vaultlist.contextMenu.lock=Blocca
main.vaultlist.contextMenu.unlock=Sblocca…
-main.vaultlist.contextMenu.unlockNow=Sblocca adesso
-main.vaultlist.contextMenu.reveal=Visualizza disco
+main.vaultlist.contextMenu.unlockNow=Sblocca Ora
+main.vaultlist.contextMenu.vaultoptions=Mostra Opzioni Cassaforte
+main.vaultlist.contextMenu.reveal=Rivela Drive
main.vaultlist.addVaultBtn=Aggiungi Cassaforte
## Vault Detail
### Welcome
diff --git a/main/ui/src/main/resources/i18n/strings_ja.properties b/main/ui/src/main/resources/i18n/strings_ja.properties
index b08180424..fca3bc5b6 100644
--- a/main/ui/src/main/resources/i18n/strings_ja.properties
+++ b/main/ui/src/main/resources/i18n/strings_ja.properties
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=ユーザー設定
addvaultwizard.new.directoryPickerButton=選択...
addvaultwizard.new.directoryPickerTitle=ディレクトリを選択
-addvaultwizard.new.fileAlreadyExists=いくつかのオブジェクトが既に存在しているため、このパスに金庫を作成することはできません。
-addvaultwizard.new.locationDoesNotExist=このパスには階層がないため金庫を作成することができません。
+addvaultwizard.new.fileAlreadyExists=金庫名と同じ名前のファイルまたはディレクトリがすでに存在しています
+addvaultwizard.new.locationDoesNotExist=指定されたパスのディレクトリが存在しないかアクセスできません
+addvaultwizard.new.locationIsNotWritable=指定されたパスに書き込み権限がありません
+addvaultwizard.new.locationIsOk=金庫に適した場所
addvaultwizard.new.invalidName=無効な金庫の名前です。一般的なディレクトリの名前を検討してください。
### Password
addvaultwizard.new.createVaultBtn=金庫を作成
@@ -98,6 +100,7 @@ unlock.passwordPrompt="%s" のパスワードを入力してください:
unlock.savePassword=パスワードを記憶させる
unlock.unlockBtn=解錠
## Success
+unlock.success.message="%s" の解錠に成功しました! 仮想ドライブから金庫にアクセス可能です。
unlock.success.rememberChoice=選択を記憶させて、再度表示しない
unlock.success.revealBtn=ドライブを表示
## Failure
@@ -173,11 +176,14 @@ preferences.updates.currentVersion=現在のバージョン: %s
preferences.updates.autoUpdateCheck=自動的に更新を確認する
preferences.updates.checkNowBtn=今すぐ確認
preferences.updates.updateAvailable=利用可能なバージョン %s に更新します。
-## Donation Key
-preferences.donationKey=寄付
-preferences.donationKey.registeredFor=%s で登録されています
-preferences.donationKey.noDonationKey=有効な Donation Key が見つかりませんでした。ライセンスキーに似ていますがフリーソフトを使う寄付者向けのキーです。 ;-)
-preferences.donationKey.getDonationKey=Donation Key を入手する
+## Contribution
+preferences.contribute=サポートする
+preferences.contribute.registeredFor=サポーター証明書が %s に登録されました
+preferences.contribute.noCertificate=Cryptomator を支援し、サポーター証明書を受け取りましょう。ライセンスキーに似ていますがフリーソフトを使う寄付者向けのキーです。 ;-)
+preferences.contribute.getCertificate=まだ証明書を手に入れていませんか? 詳細を確認しましょう。
+preferences.contribute.promptText=サポーター証明書をここに張り付けてください
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=詳細情報
@@ -222,9 +228,11 @@ main.dropZone.dropVault=この金庫を追加
main.dropZone.unknownDragboardContent=このウィンドウにドラッグして、金庫を追加
## Vault List
main.vaultlist.emptyList.onboardingInstruction=ここをクリックして金庫を追加
+main.vaultlist.contextMenu.remove=削除...
main.vaultlist.contextMenu.lock=施錠
main.vaultlist.contextMenu.unlock=解錠...
main.vaultlist.contextMenu.unlockNow=今すぐ解錠
+main.vaultlist.contextMenu.vaultoptions=金庫のオプションを表示
main.vaultlist.contextMenu.reveal=ドライブを表示
main.vaultlist.addVaultBtn=金庫を追加
## Vault Detail
diff --git a/main/ui/src/main/resources/i18n/strings_ko.properties b/main/ui/src/main/resources/i18n/strings_ko.properties
index a78e23445..40e74ebb8 100644
--- a/main/ui/src/main/resources/i18n/strings_ko.properties
+++ b/main/ui/src/main/resources/i18n/strings_ko.properties
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=사용자 지정 위치
addvaultwizard.new.directoryPickerButton=선택
addvaultwizard.new.directoryPickerTitle=디렉터리 선택
-addvaultwizard.new.fileAlreadyExists=이미 다른 객체가 존재하고 있어 해당 경로에 Vault를 생성할 수 없습니다.
-addvaultwizard.new.locationDoesNotExist=하나 이상의 경로 구성 요소가 없기 때문에 이 경로에 Vault가 생성 될 수 없습니다.
+addvaultwizard.new.fileAlreadyExists=Vault 내에 이미 존재하는 파일 또는 디렉터리 이름입니다.
+addvaultwizard.new.locationDoesNotExist=지정된 디렉터리가 존재하지 않거나 접근 할 수 없습니다.
+addvaultwizard.new.locationIsNotWritable=지정된 경로에 쓰기 권한이 없습니다.
+addvaultwizard.new.locationIsOk=Vault에 적당한 위치
addvaultwizard.new.invalidName=유효하지 않은 Vault 이름입니다. 일반적인 디렉터리 이름으로 지정해주십시요.
### Password
addvaultwizard.new.createVaultBtn=Vault 생성
@@ -98,6 +100,7 @@ unlock.passwordPrompt="%s"의 비밀번호를 입력하십시요.
unlock.savePassword=비밀번호 기억
unlock.unlockBtn=잠금해제
## Success
+unlock.success.message="%s"이(가) 성공적으로 잠금해제되었습니다. 이제 이 Vault를 가상드라이브로 접근할 수 있습니다.
unlock.success.rememberChoice=선택 기억함, 다시 묻지 않음
unlock.success.revealBtn=드라이브 표시
## Failure
@@ -173,11 +176,9 @@ preferences.updates.currentVersion=현재 버전: %s
preferences.updates.autoUpdateCheck=자동으로 업데이트 확인
preferences.updates.checkNowBtn=지금 확인
preferences.updates.updateAvailable=버전 %s의 업데이트가 가능합니다.
-## Donation Key
-preferences.donationKey=기부하기
-preferences.donationKey.registeredFor=%s (으)로 등록되어 있습니다.
-preferences.donationKey.noDonationKey=유효한 도네이션(기부) 키를 찾지 못했습니다. 라이센스 키와 비슷하나, 무료 소프트웨어를 사용하는 멋진 사람들을 위한 것입니다. ;-)
-preferences.donationKey.getDonationKey=도네이션(기부) 키 얻기
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=제품 정보
@@ -222,9 +223,11 @@ main.dropZone.dropVault=이 Vault를 추가
main.dropZone.unknownDragboardContent=Vault를 추가하려면, 이 창에 드래그 하십시요.
## Vault List
main.vaultlist.emptyList.onboardingInstruction=Vault를 추가하기 위해 이곳을 클릭합니다.
+main.vaultlist.contextMenu.remove=제거...
main.vaultlist.contextMenu.lock=잠금
main.vaultlist.contextMenu.unlock=잠금해제...
main.vaultlist.contextMenu.unlockNow=지금 잠금해제
+main.vaultlist.contextMenu.vaultoptions=Vault 옵션 보기
main.vaultlist.contextMenu.reveal=드라이브 표시
main.vaultlist.addVaultBtn=Vault 추가
## Vault Detail
diff --git a/main/ui/src/main/resources/i18n/strings_lv.properties b/main/ui/src/main/resources/i18n/strings_lv.properties
index 2812515c2..02e9e878e 100644
--- a/main/ui/src/main/resources/i18n/strings_lv.properties
+++ b/main/ui/src/main/resources/i18n/strings_lv.properties
@@ -43,7 +43,6 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Pielāgota atrašanās vieta
addvaultwizard.new.directoryPickerButton=Izvēlies...
addvaultwizard.new.directoryPickerTitle=Izvēlēties mapi
-addvaultwizard.new.fileAlreadyExists=Glabātuvi nevar izveidot šajā ceļā, jo kāds objekts jau pastāv.
addvaultwizard.new.invalidName=Nederīgs glabātuves nosaukums. Lūdzu izvēlieties vienkāršu mapes nosaukumu.
### Password
addvaultwizard.new.createVaultBtn=Izveidot glabātuvi
@@ -148,11 +147,9 @@ preferences.updates.currentVersion=Pašreizējā versija: %s
preferences.updates.autoUpdateCheck=Automātiski pārbaudīt atjauninājumus
preferences.updates.checkNowBtn=Pārbaudīt tagad
preferences.updates.updateAvailable=Pieejams atjauninājums uz versiju %s.
-## Donation Key
-preferences.donationKey=Ziedojums
-preferences.donationKey.registeredFor=Reģistrēts uz %s
-preferences.donationKey.noDonationKey=Nav atrasta derīga ziedojuma atslēga. Tā ir kā licences atslēga , bet priekš satriecošiem cilvēkiem, kas izmanto bezmaksas programmatūru. ;-)
-preferences.donationKey.getDonationKey=Iegūt ziedojuma atslēgu
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=Par lietotni
diff --git a/main/ui/src/main/resources/i18n/strings_nb.properties b/main/ui/src/main/resources/i18n/strings_nb.properties
index 444721b0b..f7c5f7c2a 100644
--- a/main/ui/src/main/resources/i18n/strings_nb.properties
+++ b/main/ui/src/main/resources/i18n/strings_nb.properties
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Tilpasset lagringssted
addvaultwizard.new.directoryPickerButton=Velg…
addvaultwizard.new.directoryPickerTitle=Velg mappe
-addvaultwizard.new.fileAlreadyExists=Hvelvet kan ikke opprettes på denne søkestien fordi et objekt allerede eksisterer.
-addvaultwizard.new.locationDoesNotExist=Hvelvet kan ikke opprettes på denne søkestien fordi minst én søkestikomponent ikke finnes.
addvaultwizard.new.invalidName=Ugyldig navn på hvelvet. Vennligst vurder et vanlig mappenavn.
### Password
addvaultwizard.new.createVaultBtn=Opprett hvelv
@@ -173,11 +171,9 @@ preferences.updates.currentVersion=Gjeldende versjon: %s
preferences.updates.autoUpdateCheck=Se etter oppdateringer automatisk
preferences.updates.checkNowBtn=Sjekk nå
preferences.updates.updateAvailable=Oppdatering til versjon %s er tilgjengelig.
-## Donation Key
-preferences.donationKey=Donasjon
-preferences.donationKey.registeredFor=Registrert for %s
-preferences.donationKey.noDonationKey=Ingen gyldig donasjonsnøkkel funnet. Det er som en lisensnøkkel, men for fantastiske mennesker som bruker gratis programvare. ;-)
-preferences.donationKey.getDonationKey=Få en donasjonsnøkkel
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=Om
diff --git a/main/ui/src/main/resources/i18n/strings_nl.properties b/main/ui/src/main/resources/i18n/strings_nl.properties
index 0d053ea2b..b6d92aad6 100644
--- a/main/ui/src/main/resources/i18n/strings_nl.properties
+++ b/main/ui/src/main/resources/i18n/strings_nl.properties
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Andere locatie
addvaultwizard.new.directoryPickerButton=Kies…
addvaultwizard.new.directoryPickerTitle=Selecteer map
-addvaultwizard.new.fileAlreadyExists=Er kan op deze locatie geen kluis aangemaakt worden, omdat er een bepaald object bestaat.
-addvaultwizard.new.locationDoesNotExist=Er kan geen kluis op dit pad aangemaakt worden omdat ten minste één onderdeel van het pad niet bestaat.
addvaultwizard.new.invalidName=Ongeldige kluisnaam. Overweeg een standaard mapnaam.
### Password
addvaultwizard.new.createVaultBtn=Kluis aanmaken
@@ -172,11 +170,9 @@ preferences.updates.currentVersion=Huidige Versie: "%s"
preferences.updates.autoUpdateCheck=Automatisch controleren op updates
preferences.updates.checkNowBtn=Controleer nu
preferences.updates.updateAvailable=Update naar versie "%s" beschikbaar.
-## Donation Key
-preferences.donationKey=Donatie
-preferences.donationKey.registeredFor=Geregistreerd voor %s
-preferences.donationKey.noDonationKey=Geen geldige donatiesleutel gevonden. Het is als een licentiesleutel, maar dan voor geweldige mensen die gratis software gebruiken. ;-)
-preferences.donationKey.getDonationKey=Verkrijg een donatiesleutel
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=Over
diff --git a/main/ui/src/main/resources/i18n/strings_nn.properties b/main/ui/src/main/resources/i18n/strings_nn.properties
index d3aab4caa..07a04fe81 100644
--- a/main/ui/src/main/resources/i18n/strings_nn.properties
+++ b/main/ui/src/main/resources/i18n/strings_nn.properties
@@ -43,8 +43,6 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Tilpassa lagringsstad
addvaultwizard.new.directoryPickerButton=Vel…
addvaultwizard.new.directoryPickerTitle=Vel mappe
-addvaultwizard.new.fileAlreadyExists=Kvelven kan ikkje opprettast på denne søkestien fordi eit objekt allereie eksisterer her.
-addvaultwizard.new.locationDoesNotExist=Kvelven kan ikkje opprettast på denne søkjestien fordi minst éin søkjestikomponent ikkje finst.
addvaultwizard.new.invalidName=Ugyldig namn på kvelven. Ver vennleg og vurder eit vanleg mappenamn.
### Password
addvaultwizard.new.createVaultBtn=Opprett kvelven
@@ -155,11 +153,9 @@ preferences.updates.currentVersion=Gjeldande versjon: %s
preferences.updates.autoUpdateCheck=Sjå etter oppdateringar automatisk
preferences.updates.checkNowBtn=Sjekk no
preferences.updates.updateAvailable=Oppdatering til versjon %s er tilgjengeleg.
-## Donation Key
-preferences.donationKey=Donasjon
-preferences.donationKey.registeredFor=Registrert for %s
-preferences.donationKey.noDonationKey=Ingen gyldig donasjonsnøkkel funne. Det er som ein lisensnøkkel, men for fantastiske menneske som bruker gratis programvare. ;-)
-preferences.donationKey.getDonationKey=Få ein donasjonsnøkkel
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=Om
diff --git a/main/ui/src/main/resources/i18n/strings_pa.properties b/main/ui/src/main/resources/i18n/strings_pa.properties
index c41562dd2..53691c763 100644
--- a/main/ui/src/main/resources/i18n/strings_pa.properties
+++ b/main/ui/src/main/resources/i18n/strings_pa.properties
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=ਪਸੰਦੀਦਾ ਟਿਕਾਣਾ
addvaultwizard.new.directoryPickerButton=…ਚੁਣੋ
addvaultwizard.new.directoryPickerTitle=ਡਾਇਰੈਕਟਰੀ ਚੁਣੋ
-addvaultwizard.new.fileAlreadyExists=ਇਸ ਮਾਰਗ ਉੱਤੇ ਵਾਲਟ ਨਹੀਂ ਬਣਾਇਆ ਜਾ ਸਕਦਾ ਹੈ, ਕਿਉਂਕਿ ਕੁਝ ਚੀਜ਼ਾਂ ਪਹਿਲਾਂ ਹੀ ਮੌਜੂਦ ਹਨ।
-addvaultwizard.new.locationDoesNotExist=ਇਸ ਮਾਰਗ ਉੱਤੇ ਵਾਲੇਟ ਨਹੀਂ ਬਣਾਇਆ ਜਾ ਸਕਦਾ, ਕਿਉਂਕਿ ਮਾਰਗ ਦਾ ਇੱਕ ਭਾਗ ਮੌਜੂਦ ਨਹੀਂ ਹੈ।
addvaultwizard.new.invalidName=ਵਾਲਟ ਦਾ ਨਾਂ ਗਲਤ ਹੈ। ਨਿਯਮਤ ਡਾਇਰੈਕਟਰੀ ਨਾਂ ਦੇਣ ਬਾਰੇ ਵਿਚਾਰੋ।
### Password
addvaultwizard.new.createVaultBtn=ਵਾਲਟ ਬਣਾਓ
@@ -173,11 +171,9 @@ preferences.updates.currentVersion=ਮੌਜੂਦਾ ਵਰਜ਼ਨ: %s
preferences.updates.autoUpdateCheck=ਅੱਪਡੇਟ ਲਈ ਆਪਣੇ-ਆਪ ਜਾਂਚ ਕਰੋ
preferences.updates.checkNowBtn=ਹੁਣੇ ਜਾਂਚ ਕਰੋ
preferences.updates.updateAvailable=ਉਪਲੱਬਧ %s ਵਰਜ਼ਨ ਲਈ ਅੱਪਡੇਟ ਕਰੋ।
-## Donation Key
-preferences.donationKey=ਦਾਨ ਦਿਓ
-preferences.donationKey.registeredFor=%s ਲਈ ਰਜਿਸਟਰ ਕਰੋ
-preferences.donationKey.noDonationKey=ਕੋਈ ਵਾਜਬ ਦਾਨ ਕੁੰਜੀ ਨਹੀਂ ਲੱਭੀ ਹੈ। ਇਹ ਲਸੰਸ ਕੁੰਜੀ ਵਾਂਗ ਹੀ ਹੈ, ਪਰ ਮੁਫ਼ਤ ਸਾਫਟਵੇਅਰ ਵਰਤਣ ਵਾਲੇ ਬੇਮਿਸਾਲ ਲੋਕਾਂ ਲਈ। ;-)
-preferences.donationKey.getDonationKey=ਦਾਨ ਕੁੰਜੀ ਦਿਓ
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=ਇਸ ਬਾਰੇ
diff --git a/main/ui/src/main/resources/i18n/strings_pl.properties b/main/ui/src/main/resources/i18n/strings_pl.properties
index 5aedad03b..4afbea35d 100644
--- a/main/ui/src/main/resources/i18n/strings_pl.properties
+++ b/main/ui/src/main/resources/i18n/strings_pl.properties
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Własna lokalizacja
addvaultwizard.new.directoryPickerButton=Wybierz…
addvaultwizard.new.directoryPickerTitle=Wybierz katalog
-addvaultwizard.new.fileAlreadyExists=Sejf nie może być utworzony w tym miejscu, ponieważ jakiś obiekt już istnieje.
-addvaultwizard.new.locationDoesNotExist=Sejf nie może być utworzony w tej ścieżce, ponieważ co najmniej jeden katalog nie istnieje.
+addvaultwizard.new.fileAlreadyExists=Plik lub katalog z nazwą sejfu już istnieje
+addvaultwizard.new.locationDoesNotExist=Katalog w określonej ścieżce nie istnieje lub nie można uzyskać dostępu
+addvaultwizard.new.locationIsNotWritable=Brak możliwości zapisu w określonej ścieżce
+addvaultwizard.new.locationIsOk=Odpowiednia lokalizacja dla Twojego sejfu
addvaultwizard.new.invalidName=Nieprawidłowa nazwa sejfu. Proszę spróbować prawidłową nazwę dla katalogu.
### Password
addvaultwizard.new.createVaultBtn=Utwórz sejf
@@ -100,7 +102,7 @@ unlock.unlockBtn=Odblokuj
## Success
unlock.success.message=Odblokowano "%s" pomyślnie! Twój sejf jest teraz dostępny za pomocą dysku wirtualnego.
unlock.success.rememberChoice=Zapamiętaj wybór, nie pokazuj tego ponownie
-unlock.success.revealBtn=Ujawnij Dysk
+unlock.success.revealBtn=Pokaż Dysk
## Failure
unlock.error.heading=Nie można odblokować sejfu
### Invalid Mount Point
@@ -174,11 +176,14 @@ preferences.updates.currentVersion=Obecna wersja: %s
preferences.updates.autoUpdateCheck=Automatycznie sprawdzaj aktualizacje
preferences.updates.checkNowBtn=Sprawdź teraz
preferences.updates.updateAvailable=Dostępna jest aktualizacja do wersji %s.
-## Donation Key
-preferences.donationKey=Dotacja
-preferences.donationKey.registeredFor=Zarejestrowano dla %s
-preferences.donationKey.noDonationKey=Nie znaleziono aktualnego klucza donacji. To taki klucz licencyjny, ale dla niesamowitych ludzi używających darmowego softu. ;-)
-preferences.donationKey.getDonationKey=Zdobądź klucz donacji
+## Contribution
+preferences.contribute=Wesprzyj nas
+preferences.contribute.registeredFor=Certyfikat darczyńcy zarejestrowany dla %s
+preferences.contribute.noCertificate=Wspomóż Cryptomator i otrzymaj certyfikat darczyńcy. Jest on jak klucz licencyjny, ale dla niesamowitych ludzi korzystających z darmowego oprogramowania. ;-)
+preferences.contribute.getCertificate=Nie masz jeszcze? Dowiedz się, jak możesz to uzyskać.
+preferences.contribute.promptText=Wklej tutaj kod certyfikatu darczyńcy
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=O programie
diff --git a/main/ui/src/main/resources/i18n/strings_pt.properties b/main/ui/src/main/resources/i18n/strings_pt.properties
index 977d610cb..eb2496525 100644
--- a/main/ui/src/main/resources/i18n/strings_pt.properties
+++ b/main/ui/src/main/resources/i18n/strings_pt.properties
@@ -43,8 +43,6 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Outro Local
addvaultwizard.new.directoryPickerButton=Escolher…
addvaultwizard.new.directoryPickerTitle=Selecionar diretório
-addvaultwizard.new.fileAlreadyExists=O cofre não pode ser criado neste diretório porque algum objeto já existe.
-addvaultwizard.new.locationDoesNotExist=O cofre não pode ser criado neste diretório porque pelo menos um componente de diretório não existe.
addvaultwizard.new.invalidName=Nome de cofre inválido. Por favor considere um nome de diretório regular.
### Password
addvaultwizard.new.createVaultBtn=Criar Cofre
@@ -120,11 +118,9 @@ preferences.updates.currentVersion=Versão atual: %s
preferences.updates.autoUpdateCheck=Verificar automaticamente por atualizações
preferences.updates.checkNowBtn=Verificar Agora
preferences.updates.updateAvailable=Atualização para a versão %s disponível.
-## Donation Key
-preferences.donationKey=Doação
-preferences.donationKey.registeredFor=Registado para %s
-preferences.donationKey.noDonationKey=Não foi encontrada uma chave de doação válida. É como uma chave de licença, mas para pessoas incríveis que usam software livre. ;-)
-preferences.donationKey.getDonationKey=Obter chave de doação
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=Sobre
diff --git a/main/ui/src/main/resources/i18n/strings_pt_BR.properties b/main/ui/src/main/resources/i18n/strings_pt_BR.properties
index 8c6f034a9..c6010b619 100644
--- a/main/ui/src/main/resources/i18n/strings_pt_BR.properties
+++ b/main/ui/src/main/resources/i18n/strings_pt_BR.properties
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Local Personalizado
addvaultwizard.new.directoryPickerButton=Escolher…
addvaultwizard.new.directoryPickerTitle=Selecionar Diretório
-addvaultwizard.new.fileAlreadyExists=O cofre não pode ser criado neste local porque algum objeto já existe.
-addvaultwizard.new.locationDoesNotExist=O cofre não pode ser criado neste caminho porque pelo menos um componente do caminho não existe.
+addvaultwizard.new.fileAlreadyExists=Um arquivo ou diretório com o nome do cofre já existe
+addvaultwizard.new.locationDoesNotExist=Um diretório no caminho especificado não existe ou não pode ser acessado
+addvaultwizard.new.locationIsNotWritable=Sem acesso de escrita no caminho especificado
+addvaultwizard.new.locationIsOk=Localização adequada para o seu cofre
addvaultwizard.new.invalidName=Nome de cofre inválido. Por favor considere usar um nome de diretório comum.
### Password
addvaultwizard.new.createVaultBtn=Criar Cofre
@@ -98,6 +100,7 @@ unlock.passwordPrompt=Digite a senha para "%s":
unlock.savePassword=Lembrar Senha
unlock.unlockBtn=Desbloquear
## Success
+unlock.success.message="%s" desbloqueado com êxito! Seu cofre agora está acessível na unidade virtual.
unlock.success.rememberChoice=Lembrar opção escolhida, não mostrar isto novamente
unlock.success.revealBtn=Revelar Volume
## Failure
@@ -173,11 +176,14 @@ preferences.updates.currentVersion=Versão atual: %s
preferences.updates.autoUpdateCheck=Buscar atualizações automaticamente
preferences.updates.checkNowBtn=Buscar Agora
preferences.updates.updateAvailable=Atualizar para versão %s disponível.
-## Donation Key
-preferences.donationKey=Doação
-preferences.donationKey.registeredFor=Registrado para %s
-preferences.donationKey.noDonationKey=Nenhuma chave de doação válida encontrada. É como uma chave de licença, mas para pessoas incríveis usando software gratuito. ;-)
-preferences.donationKey.getDonationKey=Obtenha uma chave de doação
+## Contribution
+preferences.contribute=Nos Apoie
+preferences.contribute.registeredFor=Certificado de apoiador registrado para %s
+preferences.contribute.noCertificate=Apoie o Cryptomator e receba um certificado de apoiador. Ele é uma chave de licença mas é para pessoas maravilhosas que usam software livre. ;-)
+preferences.contribute.getCertificate=Não tem um ainda? Saiba como obtê-lo.
+preferences.contribute.promptText=Cole o código do certificado de apoiador aqui
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=Sobre
@@ -222,9 +228,11 @@ main.dropZone.dropVault=Adicionar este cofre
main.dropZone.unknownDragboardContent=Se você quer adicionar um cofre, arraste-o para esta janela
## Vault List
main.vaultlist.emptyList.onboardingInstruction=Clique aqui para adicionar um cofre
+main.vaultlist.contextMenu.remove=Remover…
main.vaultlist.contextMenu.lock=Bloquear
main.vaultlist.contextMenu.unlock=Desbloquear…
main.vaultlist.contextMenu.unlockNow=Desbloquear Agora
+main.vaultlist.contextMenu.vaultoptions=Exibir Opções de Cofre
main.vaultlist.contextMenu.reveal=Revelar Volume
main.vaultlist.addVaultBtn=Adicionar Cofre
## Vault Detail
diff --git a/main/ui/src/main/resources/i18n/strings_ro.properties b/main/ui/src/main/resources/i18n/strings_ro.properties
index 9f2898f25..8dce8a60c 100644
--- a/main/ui/src/main/resources/i18n/strings_ro.properties
+++ b/main/ui/src/main/resources/i18n/strings_ro.properties
@@ -44,7 +44,9 @@
## General
## Volume
## Updates
-## Donation Key
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
# Vault Statistics
diff --git a/main/ui/src/main/resources/i18n/strings_ru.properties b/main/ui/src/main/resources/i18n/strings_ru.properties
index 7ccfc5930..40e8c460c 100644
--- a/main/ui/src/main/resources/i18n/strings_ru.properties
+++ b/main/ui/src/main/resources/i18n/strings_ru.properties
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Своё место
addvaultwizard.new.directoryPickerButton=Выбрать…
addvaultwizard.new.directoryPickerTitle=Выберите папку
-addvaultwizard.new.fileAlreadyExists=Хранилище не может быть создано по этому пути, потому что некоторые объекты уже существуют.
-addvaultwizard.new.locationDoesNotExist=Хранилище не может быть создано по этому пути, потому что по крайней мере один компонент пути не существует.
+addvaultwizard.new.fileAlreadyExists=Файл или папка с именем хранилища уже существует
+addvaultwizard.new.locationDoesNotExist=Папка с указанным адресом не существует, или недоступна
+addvaultwizard.new.locationIsNotWritable=Указанный адрес только для чтения
+addvaultwizard.new.locationIsOk=Подходящее расположение для вашего хранилища
addvaultwizard.new.invalidName=Неверное имя хранилища. Укажите корректное имя папки.
### Password
addvaultwizard.new.createVaultBtn=Создать хранилище
@@ -174,11 +176,14 @@ preferences.updates.currentVersion=Текущая версия: %s
preferences.updates.autoUpdateCheck=Автоматически проверять наличие обновлений
preferences.updates.checkNowBtn=Проверить
preferences.updates.updateAvailable=Доступно обновление до версии %s.
-## Donation Key
-preferences.donationKey=Пожертвование
-preferences.donationKey.registeredFor=Зарегистрировано: %s
-preferences.donationKey.noDonationKey=Не найден ключ пожертвования. Этот ключ похож на лицензионный, но для тех, кто использует бесплатное ПО. ;-)
-preferences.donationKey.getDonationKey=Получить ключ пожертвования
+## Contribution
+preferences.contribute=Поддержите нас
+preferences.contribute.registeredFor=Сертификат мецената зарегистрирован на имя %s
+preferences.contribute.noCertificate=Поддержите Cryptomator и получите сертификат мецената. Он действует как лицензионный ключ, но для тех, кто пользуется бесплатным ПО. ;-)
+preferences.contribute.getCertificate=Ещё нет сертификата? Узнайте, как его получить.
+preferences.contribute.promptText=Вставьте сюда код сертификата мецената
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=О программе
diff --git a/main/ui/src/main/resources/i18n/strings_sk.properties b/main/ui/src/main/resources/i18n/strings_sk.properties
index a1378658d..7578acdf2 100644
--- a/main/ui/src/main/resources/i18n/strings_sk.properties
+++ b/main/ui/src/main/resources/i18n/strings_sk.properties
@@ -43,8 +43,6 @@ addvaultwizard.new.locationLabel=Umiestnenie úložiska
addvaultwizard.new.directoryPickerLabel=Vlastné umiestnenie
addvaultwizard.new.directoryPickerButton=Vybrať…
addvaultwizard.new.directoryPickerTitle=Vybrať adresár
-addvaultwizard.new.fileAlreadyExists=Na tejto ceste nie je možné vytvoriť trezor, pretože niektorý objekt už existuje.
-addvaultwizard.new.locationDoesNotExist=Peňaženka sa nedá vytvoriť na danej ceste pretože ani jedna cesta neexistuje.
addvaultwizard.new.invalidName=Neplatný názov trezoru. Zvážte bežný názov adresára.
### Password
addvaultwizard.new.createVaultBtn=Vytvoriť trezor
@@ -96,6 +94,8 @@ unlock.passwordPrompt=Zadajte heslo pre "%s":
unlock.savePassword=Odomknúť.uložiťHeslo
unlock.unlockBtn=Odomknúť
## Success
+unlock.success.message=Odomknutie "%s" úspešné! Vaša peňaženka je už prístupná cez jej virtuálny disk.
+unlock.success.revealBtn=Odkry disk
## Failure
unlock.error.heading=Nie je možné odomknúť účet
### Invalid Mount Point
@@ -131,7 +131,14 @@ preferences.general.keychainBackend.org.cryptomator.macos.keychain.MacSystemKeyc
preferences.general.keychainBackend.org.cryptomator.windows.keychain.WindowsProtectedKeychainAccess=Windows ochrana dát
## Volume
## Updates
-## Donation Key
+## Contribution
+preferences.contribute=Podporte nás
+preferences.contribute.registeredFor=Certifikát podporovateľa registrovaný na %s
+preferences.contribute.noCertificate=Podpor Cryptomator a získaj certifikát podporovateľa. Je to ako licenčný klúč ale dobrí ľudia používajú voľný software. ;-)
+preferences.contribute.getCertificate=Ešte ho nemáš? Sleduj ako ho vieš získať.
+preferences.contribute.promptText=Vlož sem kód certifikátu podporovateľa
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
# Vault Statistics
@@ -170,9 +177,12 @@ main.preferencesBtn.tooltip=Predvoľby
main.debugModeEnabled.tooltip=Debagovací mód je aktivovaný
## Drag 'n' Drop
## Vault List
+main.vaultlist.contextMenu.remove=Odstráň…
main.vaultlist.contextMenu.lock=Uzamknúť
main.vaultlist.contextMenu.unlock=Odomknúť…
main.vaultlist.contextMenu.unlockNow=Odomknúť teraz
+main.vaultlist.contextMenu.vaultoptions=Ukáž možnosti peňaženky
+main.vaultlist.contextMenu.reveal=Odkry disk
main.vaultlist.addVaultBtn=Pridať trezor
## Vault Detail
### Welcome
@@ -181,6 +191,7 @@ main.vaultDetail.unlockBtn=Odomknúť…
main.vaultDetail.unlockNowBtn=Odomknúť teraz
main.vaultDetail.passwordSavedInKeychain=Heslo uložené
### Unlocked
+main.vaultDetail.revealBtn=Odkry disk
main.vaultDetail.lockBtn=Uzamknúť
main.vaultDetail.bytesPerSecondRead=Čítanie:
main.vaultDetail.bytesPerSecondWritten=Zápis:
@@ -195,6 +206,7 @@ main.vaultDetail.missing.remove=Odstrániť zo zoznamu peňaženky…
# Vault Options
## General
vaultOptions.general.vaultName=Názov trezoru
+vaultOptions.general.actionAfterUnlock.reveal=Odkry disk
## Mount
vaultOptions.mount.mountPoint.directoryPickerButton=Vybrať…
## Master Key
diff --git a/main/ui/src/main/resources/i18n/strings_sr.properties b/main/ui/src/main/resources/i18n/strings_sr.properties
index 9f2898f25..8dce8a60c 100644
--- a/main/ui/src/main/resources/i18n/strings_sr.properties
+++ b/main/ui/src/main/resources/i18n/strings_sr.properties
@@ -44,7 +44,9 @@
## General
## Volume
## Updates
-## Donation Key
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
# Vault Statistics
diff --git a/main/ui/src/main/resources/i18n/strings_sr_Latn.properties b/main/ui/src/main/resources/i18n/strings_sr_Latn.properties
new file mode 100644
index 000000000..8dce8a60c
--- /dev/null
+++ b/main/ui/src/main/resources/i18n/strings_sr_Latn.properties
@@ -0,0 +1,77 @@
+# Locale Specific CSS files such as CJK, RTL,...
+
+# Generics
+## Button
+## Error
+
+# Defaults
+
+# Tray Menu
+
+# Add Vault Wizard
+## Welcome
+## New
+### Name
+### Location
+### Password
+### Information
+## Existing
+## Success
+
+# Remove Vault
+
+# Change Password
+
+# Forget Password
+
+# Unlock
+## Success
+## Failure
+### Invalid Mount Point
+
+# Lock
+## Force
+## Failure
+
+# Migration
+## Start
+## Run
+## Sucess
+## Missing file system capabilities
+## Impossible
+
+# Preferences
+## General
+## Volume
+## Updates
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
+## About
+
+# Vault Statistics
+## Read
+## Write
+
+# Main Window
+## Drag 'n' Drop
+## Vault List
+## Vault Detail
+### Welcome
+### Locked
+### Unlocked
+### Missing
+### Needs Migration
+
+# Wrong File Alert
+
+# Vault Options
+## General
+## Mount
+## Master Key
+
+# Recovery Key
+
+# New Password
+
+# Quit
diff --git a/main/ui/src/main/resources/i18n/strings_sv.properties b/main/ui/src/main/resources/i18n/strings_sv.properties
index 54b9dd28c..672698152 100644
--- a/main/ui/src/main/resources/i18n/strings_sv.properties
+++ b/main/ui/src/main/resources/i18n/strings_sv.properties
@@ -44,8 +44,6 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Anpassad plats
addvaultwizard.new.directoryPickerButton=Välj…
addvaultwizard.new.directoryPickerTitle=Välj katalog
-addvaultwizard.new.fileAlreadyExists=Valvet kan inte skapas på denna plats då vissa filer redan existerar.
-addvaultwizard.new.locationDoesNotExist=Valvet kan inte skapas på den här platsen då åtminstone en komponent inte finns.
addvaultwizard.new.invalidName=Felaktigt namn på valvet. Vänligen ange ett vanligt katalognamn.
### Password
addvaultwizard.new.createVaultBtn=Skapa Valv
@@ -98,6 +96,7 @@ unlock.passwordPrompt=Ange lösenord för "%s":
unlock.savePassword=Kom ihåg lösenord
unlock.unlockBtn=Lås upp
## Success
+unlock.success.message="%s" upplåst! Ditt valv är nu åtkomligt från den virtuella enheten.
unlock.success.rememberChoice=Kom ihåg valet, visa inte detta igen
unlock.success.revealBtn=Visa enhet
## Failure
@@ -173,11 +172,9 @@ preferences.updates.currentVersion=Nuvarande version: %s
preferences.updates.autoUpdateCheck=Sök efter uppdateringar automatiskt
preferences.updates.checkNowBtn=Kontrollera nu
preferences.updates.updateAvailable=Uppdatering till version %s finns tillgänglig.
-## Donation Key
-preferences.donationKey=Donation
-preferences.donationKey.registeredFor=Registrerad till %s
-preferences.donationKey.noDonationKey=Ingen giltig donationsnyckel funnen. Det är som en licensnyckel fast för grymma personer som kör fri mjukvara ;-)
-preferences.donationKey.getDonationKey=Skaffa en donationsnyckel
+## Contribution
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=Om
@@ -222,9 +219,11 @@ main.dropZone.dropVault=Lägg till detta valv
main.dropZone.unknownDragboardContent=Om du vill lägga till ett valv, dra in det till detta fönster
## Vault List
main.vaultlist.emptyList.onboardingInstruction=Klicka här för att lägga till valv
+main.vaultlist.contextMenu.remove=Ta bort…
main.vaultlist.contextMenu.lock=Lås
main.vaultlist.contextMenu.unlock=Lås upp…
main.vaultlist.contextMenu.unlockNow=Lås upp nu
+main.vaultlist.contextMenu.vaultoptions=Visa inställningar för valv
main.vaultlist.contextMenu.reveal=Visa enhet
main.vaultlist.addVaultBtn=Lägg till valv
## Vault Detail
diff --git a/main/ui/src/main/resources/i18n/strings_tr.properties b/main/ui/src/main/resources/i18n/strings_tr.properties
index 3b46959d9..8439cb797 100644
--- a/main/ui/src/main/resources/i18n/strings_tr.properties
+++ b/main/ui/src/main/resources/i18n/strings_tr.properties
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=Özel Konum
addvaultwizard.new.directoryPickerButton=Seç…
addvaultwizard.new.directoryPickerTitle=Dizin Seç
-addvaultwizard.new.fileAlreadyExists=Bazı nesneler varolduğu için kasa bu yolda oluşturulamıyor.
-addvaultwizard.new.locationDoesNotExist=En az bir yol bileşeni varolmadığı için kasa bu yolda oluşturulamıyor.
+addvaultwizard.new.fileAlreadyExists=Kasa adına sahip bir dosya ya da dizin zaten mevcut
+addvaultwizard.new.locationDoesNotExist=Belirtilen yoldaki bir dizin mevcut değil veya erişilemez
+addvaultwizard.new.locationIsNotWritable=Belirtilen yolda yazma erişimi yok
+addvaultwizard.new.locationIsOk=Kasanız için uygun yer
addvaultwizard.new.invalidName=Geçersiz kasa adı. Lütfen normal bir dizin adı kullanın.
### Password
addvaultwizard.new.createVaultBtn=Kasa Oluştur
@@ -174,11 +176,14 @@ preferences.updates.currentVersion=Şuanki Sürüm: %s
preferences.updates.autoUpdateCheck=Güncellemeleri otomatik kontrol et
preferences.updates.checkNowBtn=Şimdi Kontrol Et
preferences.updates.updateAvailable=%s sürümüne güncelleme mevcut.
-## Donation Key
-preferences.donationKey=Bağış
-preferences.donationKey.registeredFor=%s için kaydedildi
-preferences.donationKey.noDonationKey=Geçerli bir bağış anahtarı bulunamadı. Bu anahtar, lisans anahtarı gibidir ama ücretsiz uygulama kullanan muhteşem insanlar içindir ;-)
-preferences.donationKey.getDonationKey=Bir bağış anahtarı al
+## Contribution
+preferences.contribute=Bizi Destekle
+preferences.contribute.registeredFor=%s için destekçi sertifikası kaydedildi
+preferences.contribute.noCertificate=Cryptomator'u destekle ve bir destekçi sertifikası al. Bu bir lisans anahtarı gibidir ama ücretsiz program kullanan müthiş insanlar için. ;-)
+preferences.contribute.getCertificate=Herhangi bir tane yok mu? Nasıl elde edebileceğinizi öğrenin.
+preferences.contribute.promptText=Destekçi sertifika kodunu buraya yapıştırın
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=Hakkında
diff --git a/main/ui/src/main/resources/i18n/strings_zh.properties b/main/ui/src/main/resources/i18n/strings_zh.properties
index a6227e9f7..21be7c966 100644
--- a/main/ui/src/main/resources/i18n/strings_zh.properties
+++ b/main/ui/src/main/resources/i18n/strings_zh.properties
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=自定义位置
addvaultwizard.new.directoryPickerButton=选择...
addvaultwizard.new.directoryPickerTitle=选择目录
-addvaultwizard.new.fileAlreadyExists=无法在此路径上创建保险库,因为某些对象已经存在。
-addvaultwizard.new.locationDoesNotExist=无法在此路径上创建保险库,因为至少有一个路径组件不存在
+addvaultwizard.new.fileAlreadyExists=已存在同名文件或文件夹
+addvaultwizard.new.locationDoesNotExist=指定路径中的目录不存在或无法访问
+addvaultwizard.new.locationIsNotWritable=没有对指定路径的写入权限
+addvaultwizard.new.locationIsOk=合适的保险库路径
addvaultwizard.new.invalidName=无效的保险库名称,请考虑一个常规的目录名称
### Password
addvaultwizard.new.createVaultBtn=创建保险库
@@ -98,6 +100,7 @@ unlock.passwordPrompt=输入 "%s" 的密码
unlock.savePassword=记住密码
unlock.unlockBtn=解锁
## Success
+unlock.success.message=已成功解锁 "%s"! 您现在可以通过其虚拟驱动器访问它
unlock.success.rememberChoice=记住选项且不再显示
unlock.success.revealBtn=显示驱动器
## Failure
@@ -173,11 +176,14 @@ preferences.updates.currentVersion=当前版本:%s
preferences.updates.autoUpdateCheck=自动检查更新
preferences.updates.checkNowBtn=立即检查
preferences.updates.updateAvailable=可更新到版本 %s
-## Donation Key
-preferences.donationKey=捐赠
-preferences.donationKey.registeredFor=已注册给 %s
-preferences.donationKey.noDonationKey=找不到有效的捐赠密钥,它相当于一个专门给使用免费软件的帅哥美女的许可证密钥 ;-)
-preferences.donationKey.getDonationKey=获取一个捐赠密钥
+## Contribution
+preferences.contribute=支持我们
+preferences.contribute.registeredFor=已为 %s 注册支持者证书
+preferences.contribute.noCertificate=支持 Cryptomator 并获得一份支持者证书。它类似于许可证密钥,特别之处是提供给使用免费软件的牛人的 ;-)
+preferences.contribute.getCertificate=还没有该证书吗?了解您如何获取
+preferences.contribute.promptText=请在此粘贴支持者证书代码
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=关于
@@ -222,9 +228,11 @@ main.dropZone.dropVault=添加此保险库
main.dropZone.unknownDragboardContent=如果您想要添加一个保险库,将其拖动到此窗口
## Vault List
main.vaultlist.emptyList.onboardingInstruction=点击此处添加一个保险库
+main.vaultlist.contextMenu.remove=删除…
main.vaultlist.contextMenu.lock=锁定
main.vaultlist.contextMenu.unlock=解锁…
main.vaultlist.contextMenu.unlockNow=立即解锁
+main.vaultlist.contextMenu.vaultoptions=显示保险库选项
main.vaultlist.contextMenu.reveal=显示驱动器
main.vaultlist.addVaultBtn=添加保险库
## Vault Detail
diff --git a/main/ui/src/main/resources/i18n/strings_zh_TW.properties b/main/ui/src/main/resources/i18n/strings_zh_TW.properties
index c1afb3720..1378294b7 100644
--- a/main/ui/src/main/resources/i18n/strings_zh_TW.properties
+++ b/main/ui/src/main/resources/i18n/strings_zh_TW.properties
@@ -44,8 +44,10 @@ addvaultwizard.new.locationPrompt=…
addvaultwizard.new.directoryPickerLabel=自訂位置
addvaultwizard.new.directoryPickerButton=選取
addvaultwizard.new.directoryPickerTitle=選取資料夾
-addvaultwizard.new.fileAlreadyExists=無法在這個路徑建立加密檔案庫,因為其中已有其他檔案。
-addvaultwizard.new.locationDoesNotExist=無法在這個路徑建立加密檔案庫,因為部份路徑不存在。
+addvaultwizard.new.fileAlreadyExists=存在與加密檔案庫同名的檔案或資料夾
+addvaultwizard.new.locationDoesNotExist=指定的目錄不存在或無法存取
+addvaultwizard.new.locationIsNotWritable=指定路徑沒有寫入權限
+addvaultwizard.new.locationIsOk=可放置您的加密檔案庫
addvaultwizard.new.invalidName=無效的加密檔案庫名稱。請考慮一般資料夾會使用的名稱。
### Password
addvaultwizard.new.createVaultBtn=新建加密檔案庫
@@ -174,11 +176,14 @@ preferences.updates.currentVersion=目前版本:%s
preferences.updates.autoUpdateCheck=勾選以啟用自動更新
preferences.updates.checkNowBtn=立即檢查
preferences.updates.updateAvailable=有版本 %s 可更新。
-## Donation Key
-preferences.donationKey=贊助
-preferences.donationKey.registeredFor=註冊給 %s
-preferences.donationKey.noDonationKey=未發現贊助金鑰。贊助金鑰就像授權金鑰,不過是給使用自由軟體的善心人士的。
-preferences.donationKey.getDonationKey=取得贊助金鑰
+## Contribution
+preferences.contribute=贊助我們
+preferences.contribute.registeredFor=贊助憑證註冊給 %s
+preferences.contribute.noCertificate=支持 Cryptomator 並取得一個贊助憑證。它就像一個授權金鑰,但是給使用自由軟體的大好人的。;-)
+preferences.contribute.getCertificate=還沒有嗎?看看您該如何取得它。
+preferences.contribute.promptText=在這裡貼上贊助憑證
+#<-- Add entries for donations and code/translation/documentation contribution -->
+
## About
preferences.about=關於
diff --git a/main/ui/src/main/resources/license/THIRD-PARTY.txt b/main/ui/src/main/resources/license/THIRD-PARTY.txt
index 0f8241697..8b657b18d 100644
--- a/main/ui/src/main/resources/license/THIRD-PARTY.txt
+++ b/main/ui/src/main/resources/license/THIRD-PARTY.txt
@@ -61,10 +61,10 @@ Cryptomator uses 45 third-party dependencies under the following licenses:
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix)
GPLv2+CE:
- Java Servlet API (javax.servlet:javax.servlet-api:4.0.1 - https://javaee.github.io/servlet-spec/)
- - javafx-base (org.openjfx:javafx-base:15 - https://openjdk.java.net/projects/openjfx/javafx-base/)
- - javafx-controls (org.openjfx:javafx-controls:15 - https://openjdk.java.net/projects/openjfx/javafx-controls/)
- - javafx-fxml (org.openjfx:javafx-fxml:15 - https://openjdk.java.net/projects/openjfx/javafx-fxml/)
- - javafx-graphics (org.openjfx:javafx-graphics:15 - https://openjdk.java.net/projects/openjfx/javafx-graphics/)
+ - javafx-base (org.openjfx:javafx-base:16 - https://openjdk.java.net/projects/openjfx/javafx-base/)
+ - javafx-controls (org.openjfx:javafx-controls:16 - https://openjdk.java.net/projects/openjfx/javafx-controls/)
+ - javafx-fxml (org.openjfx:javafx-fxml:16 - https://openjdk.java.net/projects/openjfx/javafx-fxml/)
+ - javafx-graphics (org.openjfx:javafx-graphics:16 - https://openjdk.java.net/projects/openjfx/javafx-graphics/)
LGPL 2.1:
- jnr-posix (com.github.jnr:jnr-posix:3.0.54 - http://nexus.sonatype.org/oss-repository-hosting.html/jnr-posix)
- Java Native Access (net.java.dev.jna:jna:5.7.0 - https://github.com/java-native-access/jna)