mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-09-23 16:34:34 +00:00
Compare commits
50
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6d88f1dc4 | ||
|
|
4a2d71405d | ||
|
|
f98c3aa5ca | ||
|
|
7d389b561d | ||
|
|
7056d35254 | ||
|
|
6a8234ddd9 | ||
|
|
3d3eed0ed2 | ||
|
|
e479b35846 | ||
|
|
83c7421a96 | ||
|
|
116091f2e0 | ||
|
|
ce11017609 | ||
|
|
caa8c84d8a | ||
|
|
e14fc56b37 | ||
|
|
0d29e56948 | ||
|
|
d2a27c782d | ||
|
|
864e92ea59 | ||
|
|
97dfe9a1d4 | ||
|
|
d7edfd13a7 | ||
|
|
35207de7cc | ||
|
|
adf0e3720d | ||
|
|
f73c1889b7 | ||
|
|
18ff1d2898 | ||
|
|
c6e2fa11a4 | ||
|
|
3fac8b7cd6 | ||
|
|
f62faa72ce | ||
|
|
57256d0733 | ||
|
|
e1f44fb48a | ||
|
|
62676d5a83 | ||
|
|
7755256956 | ||
|
|
e1cf8546b7 | ||
|
|
e40837da2c | ||
|
|
e7c3c0ab53 | ||
|
|
2336b9c622 | ||
|
|
4e2122a64f | ||
|
|
833bb085e6 | ||
|
|
1ec9b0507d | ||
|
|
b6a4f70ec9 | ||
|
|
eb047e29a0 | ||
|
|
5dc1d9b2e0 | ||
|
|
362f3eac63 | ||
|
|
f0b26c60c4 | ||
|
|
e827d86967 | ||
|
|
04533c0338 | ||
|
|
e148e39fef | ||
|
|
cb97905c98 | ||
|
|
7ec7cecbfd | ||
|
|
3369401f1c | ||
|
|
09b9cfe027 | ||
|
|
e94ac3720b | ||
|
|
4414d60026 |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# These are supported funding model platforms
|
# These are supported funding model platforms
|
||||||
|
|
||||||
github: [overheadhunter, tobihagemann] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
github: [cryptomator] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||||
patreon: # Replace with a single Patreon username
|
patreon: # Replace with a single Patreon username
|
||||||
open_collective: # Replace with a single Open Collective username
|
open_collective: # Replace with a single Open Collective username
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
ko_fi: # Replace with a single Ko-fi username
|
||||||
|
|||||||
Generated
+6
-6
@@ -7,20 +7,20 @@
|
|||||||
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
<sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
|
||||||
<outputRelativeToContentRoot value="true" />
|
<outputRelativeToContentRoot value="true" />
|
||||||
<processorPath useClasspath="false">
|
<processorPath useClasspath="false">
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.25.2/dagger-compiler-2.25.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-compiler/2.26/dagger-compiler-2.26.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.25.2/dagger-2.25.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger/2.26/dagger-2.26.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/javax/inject/javax.inject/1/javax.inject-1.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.25.2/dagger-producers-2.25.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-producers/2.26/dagger-producers-2.26.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/27.1-jre/guava-27.1-jre.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/guava/guava/27.1-jre/guava-27.1-jre.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/code/findbugs/jsr305/3.0.1/jsr305-3.0.1.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-qual/2.5.2/checker-qual-2.5.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-qual/2.5.2/checker-qual-2.5.2.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.2.0/error_prone_annotations-2.2.0.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/error_prone_annotations/2.2.0/error_prone_annotations-2.2.0.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/1.1/j2objc-annotations-1.1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/j2objc/j2objc-annotations/1.1/j2objc-annotations-1.1.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/codehaus/mojo/animal-sniffer-annotations/1.17/animal-sniffer-annotations-1.17.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/codehaus/mojo/animal-sniffer-annotations/1.17/animal-sniffer-annotations-1.17.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.3/checker-compat-qual-2.5.3.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/checkerframework/checker-compat-qual/2.5.3/checker-compat-qual-2.5.3.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.25.2/dagger-spi-2.25.2.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/dagger/dagger-spi/2.26/dagger-spi-2.26.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.11.1/javapoet-1.11.1.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/squareup/javapoet/1.11.1/javapoet-1.11.1.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/googlejavaformat/google-java-format/1.5/google-java-format-1.5.jar" />
|
||||||
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
|
<entry name="$MAVEN_REPOSITORY$/com/google/errorprone/javac-shaded/9-dev-r4023-3/javac-shaded-9-dev-r4023-3.jar" />
|
||||||
@@ -32,9 +32,9 @@
|
|||||||
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-metadata-jvm/0.1.0/kotlinx-metadata-jvm-0.1.0.jar" />
|
<entry name="$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-metadata-jvm/0.1.0/kotlinx-metadata-jvm-0.1.0.jar" />
|
||||||
</processorPath>
|
</processorPath>
|
||||||
<module name="keychain" />
|
<module name="keychain" />
|
||||||
<module name="launcher" />
|
|
||||||
<module name="commons" />
|
<module name="commons" />
|
||||||
<module name="ui" />
|
<module name="ui" />
|
||||||
|
<module name="launcher" />
|
||||||
</profile>
|
</profile>
|
||||||
</annotationProcessing>
|
</annotationProcessing>
|
||||||
</component>
|
</component>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.5.0-beta2</version>
|
<version>1.5.0-beta3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>buildkit</artifactId>
|
<artifactId>buildkit</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.5.0-beta2</version>
|
<version>1.5.0-beta3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>commons</artifactId>
|
<artifactId>commons</artifactId>
|
||||||
<name>Cryptomator Commons</name>
|
<name>Cryptomator Commons</name>
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package org.cryptomator.common;
|
package org.cryptomator.common;
|
||||||
|
|
||||||
import dagger.Binds;
|
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import javafx.beans.binding.Binding;
|
import javafx.beans.binding.Binding;
|
||||||
@@ -19,6 +18,8 @@ import org.cryptomator.common.vaults.VaultComponent;
|
|||||||
import org.cryptomator.common.vaults.VaultListManager;
|
import org.cryptomator.common.vaults.VaultListManager;
|
||||||
import org.cryptomator.frontend.webdav.WebDavServer;
|
import org.cryptomator.frontend.webdav.WebDavServer;
|
||||||
import org.fxmisc.easybind.EasyBind;
|
import org.fxmisc.easybind.EasyBind;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
@@ -27,12 +28,18 @@ import java.util.Comparator;
|
|||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.SynchronousQueue;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
@Module(subcomponents = {VaultComponent.class})
|
@Module(subcomponents = {VaultComponent.class})
|
||||||
public abstract class CommonsModule {
|
public abstract class CommonsModule {
|
||||||
|
|
||||||
private static final int NUM_SCHEDULER_THREADS = 4;
|
private static final Logger LOG = LoggerFactory.getLogger(CommonsModule.class);
|
||||||
|
private static final int NUM_SCHEDULER_THREADS = 2;
|
||||||
|
private static final int NUM_CORE_BG_THREADS = 6;
|
||||||
|
private static final long BG_THREAD_KEEPALIVE_SECONDS = 60l;
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@@ -69,18 +76,38 @@ public abstract class CommonsModule {
|
|||||||
static ScheduledExecutorService provideScheduledExecutorService(ShutdownHook shutdownHook) {
|
static ScheduledExecutorService provideScheduledExecutorService(ShutdownHook shutdownHook) {
|
||||||
final AtomicInteger threadNumber = new AtomicInteger(1);
|
final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||||
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(NUM_SCHEDULER_THREADS, r -> {
|
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(NUM_SCHEDULER_THREADS, r -> {
|
||||||
|
String name = String.format("App Scheduled Executor %02d", threadNumber.getAndIncrement());
|
||||||
Thread t = new Thread(r);
|
Thread t = new Thread(r);
|
||||||
t.setName("Background Thread " + threadNumber.getAndIncrement());
|
t.setName(name);
|
||||||
|
t.setUncaughtExceptionHandler(CommonsModule::handleUncaughtExceptionInBackgroundThread);
|
||||||
t.setDaemon(true);
|
t.setDaemon(true);
|
||||||
|
LOG.debug("Starting {}", t.getName());
|
||||||
return t;
|
return t;
|
||||||
});
|
});
|
||||||
shutdownHook.runOnShutdown(executorService::shutdown);
|
shutdownHook.runOnShutdown(executorService::shutdown);
|
||||||
return executorService;
|
return executorService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Binds
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
abstract ExecutorService bindExecutorService(ScheduledExecutorService executor);
|
static ExecutorService provideExecutorService(ShutdownHook shutdownHook) {
|
||||||
|
final AtomicInteger threadNumber = new AtomicInteger(1);
|
||||||
|
ExecutorService executorService = new ThreadPoolExecutor(NUM_CORE_BG_THREADS, Integer.MAX_VALUE, BG_THREAD_KEEPALIVE_SECONDS, TimeUnit.SECONDS, new SynchronousQueue<>(), r -> {
|
||||||
|
String name = String.format("App Background Thread %03d", threadNumber.getAndIncrement());
|
||||||
|
Thread t = new Thread(r);
|
||||||
|
t.setName(name);
|
||||||
|
t.setUncaughtExceptionHandler(CommonsModule::handleUncaughtExceptionInBackgroundThread);
|
||||||
|
t.setDaemon(true);
|
||||||
|
LOG.debug("Starting {}", t.getName());
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
shutdownHook.runOnShutdown(executorService::shutdown);
|
||||||
|
return executorService;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void handleUncaughtExceptionInBackgroundThread(Thread thread, Throwable throwable) {
|
||||||
|
LOG.error("Uncaught exception in " + thread.getName(), throwable);
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import java.nio.file.Path;
|
|||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
@@ -46,16 +45,17 @@ public class SettingsProvider implements Supplier<Settings> {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
|
private static final Logger LOG = LoggerFactory.getLogger(SettingsProvider.class);
|
||||||
private static final long SAVE_DELAY_MS = 1000;
|
private static final long SAVE_DELAY_MS = 1000;
|
||||||
|
|
||||||
private final ScheduledExecutorService saveScheduler = Executors.newSingleThreadScheduledExecutor();
|
|
||||||
private final AtomicReference<ScheduledFuture<?>> scheduledSaveCmd = new AtomicReference<>();
|
private final AtomicReference<ScheduledFuture<?>> scheduledSaveCmd = new AtomicReference<>();
|
||||||
private final AtomicReference<Settings> settings = new AtomicReference<>();
|
private final AtomicReference<Settings> settings = new AtomicReference<>();
|
||||||
private final SettingsJsonAdapter settingsJsonAdapter = new SettingsJsonAdapter();
|
private final SettingsJsonAdapter settingsJsonAdapter = new SettingsJsonAdapter();
|
||||||
private final Environment env;
|
private final Environment env;
|
||||||
|
private final ScheduledExecutorService scheduler;
|
||||||
private final Gson gson;
|
private final Gson gson;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public SettingsProvider(Environment env) {
|
public SettingsProvider(Environment env, ScheduledExecutorService scheduler) {
|
||||||
this.env = env;
|
this.env = env;
|
||||||
|
this.scheduler = scheduler;
|
||||||
this.gson = new GsonBuilder() //
|
this.gson = new GsonBuilder() //
|
||||||
.setPrettyPrinting().setLenient().disableHtmlEscaping() //
|
.setPrettyPrinting().setLenient().disableHtmlEscaping() //
|
||||||
.registerTypeAdapter(Settings.class, settingsJsonAdapter) //
|
.registerTypeAdapter(Settings.class, settingsJsonAdapter) //
|
||||||
@@ -98,7 +98,7 @@ public class SettingsProvider implements Supplier<Settings> {
|
|||||||
final Optional<Path> settingsPath = env.getSettingsPath().findFirst(); // alway save to preferred (first) path
|
final Optional<Path> settingsPath = env.getSettingsPath().findFirst(); // alway save to preferred (first) path
|
||||||
settingsPath.ifPresent(path -> {
|
settingsPath.ifPresent(path -> {
|
||||||
Runnable saveCommand = () -> this.save(settings, path);
|
Runnable saveCommand = () -> this.save(settings, path);
|
||||||
ScheduledFuture<?> scheduledTask = saveScheduler.schedule(saveCommand, SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
|
ScheduledFuture<?> scheduledTask = scheduler.schedule(saveCommand, SAVE_DELAY_MS, TimeUnit.MILLISECONDS);
|
||||||
ScheduledFuture<?> previouslyScheduledTask = scheduledSaveCmd.getAndSet(scheduledTask);
|
ScheduledFuture<?> previouslyScheduledTask = scheduledSaveCmd.getAndSet(scheduledTask);
|
||||||
if (previouslyScheduledTask != null) {
|
if (previouslyScheduledTask != null) {
|
||||||
previouslyScheduledTask.cancel(false);
|
previouslyScheduledTask.cancel(false);
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ public final class WindowsDriveLetters {
|
|||||||
|
|
||||||
public Set<String> getOccupiedDriveLetters() {
|
public Set<String> getOccupiedDriveLetters() {
|
||||||
if (!SystemUtils.IS_OS_WINDOWS) {
|
if (!SystemUtils.IS_OS_WINDOWS) {
|
||||||
LOG.warn("Attempted to get occupied drive letters on non-Windows machine.");
|
|
||||||
return Set.of();
|
return Set.of();
|
||||||
} else {
|
} else {
|
||||||
Iterable<Path> rootDirs = FileSystems.getDefault().getRootDirectories();
|
Iterable<Path> rootDirs = FileSystems.getDefault().getRootDirectories();
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.5.0-beta2</version>
|
<version>1.5.0-beta3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>keychain</artifactId>
|
<artifactId>keychain</artifactId>
|
||||||
<name>System Keychain Access</name>
|
<name>System Keychain Access</name>
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import dagger.Provides;
|
|||||||
import dagger.multibindings.ElementsIntoSet;
|
import dagger.multibindings.ElementsIntoSet;
|
||||||
import org.cryptomator.common.JniModule;
|
import org.cryptomator.common.JniModule;
|
||||||
|
|
||||||
|
import javax.inject.Singleton;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@@ -24,6 +25,7 @@ public class KeychainModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
|
@Singleton
|
||||||
public Optional<KeychainAccess> provideSupportedKeychain(Set<KeychainAccessStrategy> keychainAccessStrategies) {
|
public Optional<KeychainAccess> provideSupportedKeychain(Set<KeychainAccessStrategy> keychainAccessStrategies) {
|
||||||
return keychainAccessStrategies.stream().filter(KeychainAccessStrategy::isSupported).map(KeychainAccess.class::cast).findFirst();
|
return keychainAccessStrategies.stream().filter(KeychainAccessStrategy::isSupported).map(KeychainAccess.class::cast).findFirst();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.5.0-beta2</version>
|
<version>1.5.0-beta3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>launcher</artifactId>
|
<artifactId>launcher</artifactId>
|
||||||
<name>Cryptomator Launcher</name>
|
<name>Cryptomator Launcher</name>
|
||||||
|
|||||||
@@ -21,8 +21,10 @@ import java.nio.file.FileSystems;
|
|||||||
import java.nio.file.InvalidPathException;
|
import java.nio.file.InvalidPathException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@@ -40,7 +42,7 @@ class FileOpenRequestHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void openFiles(OpenFilesEvent evt) {
|
private void openFiles(OpenFilesEvent evt) {
|
||||||
Stream<Path> pathsToOpen = evt.getFiles().stream().map(File::toPath);
|
Collection<Path> pathsToOpen = evt.getFiles().stream().map(File::toPath).collect(Collectors.toList());
|
||||||
AppLaunchEvent launchEvent = new AppLaunchEvent(AppLaunchEvent.EventType.OPEN_FILE, pathsToOpen);
|
AppLaunchEvent launchEvent = new AppLaunchEvent(AppLaunchEvent.EventType.OPEN_FILE, pathsToOpen);
|
||||||
tryToEnqueueFileOpenRequest(launchEvent);
|
tryToEnqueueFileOpenRequest(launchEvent);
|
||||||
}
|
}
|
||||||
@@ -51,16 +53,18 @@ class FileOpenRequestHandler {
|
|||||||
|
|
||||||
// visible for testing
|
// visible for testing
|
||||||
void handleLaunchArgs(FileSystem fs, String[] args) {
|
void handleLaunchArgs(FileSystem fs, String[] args) {
|
||||||
Stream<Path> pathsToOpen = Arrays.stream(args).map(str -> {
|
Collection<Path> pathsToOpen = Arrays.stream(args).map(str -> {
|
||||||
try {
|
try {
|
||||||
return fs.getPath(str);
|
return fs.getPath(str);
|
||||||
} catch (InvalidPathException e) {
|
} catch (InvalidPathException e) {
|
||||||
LOG.trace("Argument not a valid path: {}", str);
|
LOG.trace("Argument not a valid path: {}", str);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}).filter(Objects::nonNull);
|
}).filter(Objects::nonNull).collect(Collectors.toList());
|
||||||
AppLaunchEvent launchEvent = new AppLaunchEvent(AppLaunchEvent.EventType.OPEN_FILE, pathsToOpen);
|
if (!pathsToOpen.isEmpty()) {
|
||||||
tryToEnqueueFileOpenRequest(launchEvent);
|
AppLaunchEvent launchEvent = new AppLaunchEvent(AppLaunchEvent.EventType.OPEN_FILE, pathsToOpen);
|
||||||
|
tryToEnqueueFileOpenRequest(launchEvent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import javax.inject.Inject;
|
|||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -27,7 +28,7 @@ class IpcProtocolImpl implements IpcProtocol {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void revealRunningApp() {
|
public void revealRunningApp() {
|
||||||
launchEventQueue.add(new AppLaunchEvent(AppLaunchEvent.EventType.REVEAL_APP, Stream.empty()));
|
launchEventQueue.add(new AppLaunchEvent(AppLaunchEvent.EventType.REVEAL_APP, Collections.emptyList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -55,10 +55,11 @@ public class LoggerConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// configure upgrade logger:
|
// configure upgrade logger:
|
||||||
Logger upgrades = context.getLogger("org.cryptomator.ui.model.upgrade");
|
Logger upgrades = context.getLogger("org.cryptomator.cryptofs.migration");
|
||||||
upgrades.setLevel(Level.DEBUG);
|
upgrades.setLevel(Level.DEBUG);
|
||||||
upgrades.addAppender(stdout);
|
upgrades.addAppender(stdout);
|
||||||
upgrades.addAppender(upgrade);
|
upgrades.addAppender(upgrade);
|
||||||
|
upgrades.addAppender(file);
|
||||||
upgrades.setAdditive(false);
|
upgrades.setAdditive(false);
|
||||||
|
|
||||||
// add shutdown hook
|
// add shutdown hook
|
||||||
|
|||||||
+8
-12
@@ -15,16 +15,14 @@ import org.junit.jupiter.api.DisplayName;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.FileSystem;
|
import java.nio.file.FileSystem;
|
||||||
import java.nio.file.InvalidPathException;
|
import java.nio.file.InvalidPathException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.List;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
public class FileOpenRequestHandlerTest {
|
public class FileOpenRequestHandlerTest {
|
||||||
|
|
||||||
@@ -39,32 +37,30 @@ public class FileOpenRequestHandlerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("./cryptomator.exe foo bar")
|
@DisplayName("./cryptomator.exe foo bar")
|
||||||
public void testOpenArgsWithCorrectPaths() throws IOException {
|
public void testOpenArgsWithCorrectPaths() {
|
||||||
inTest.handleLaunchArgs(new String[]{"foo", "bar"});
|
inTest.handleLaunchArgs(new String[]{"foo", "bar"});
|
||||||
|
|
||||||
AppLaunchEvent evt = queue.poll();
|
AppLaunchEvent evt = queue.poll();
|
||||||
Assertions.assertNotNull(evt);
|
Assertions.assertNotNull(evt);
|
||||||
List<Path> paths = evt.getPathsToOpen().collect(Collectors.toList());
|
Collection<Path> paths = evt.getPathsToOpen();
|
||||||
MatcherAssert.assertThat(paths, CoreMatchers.hasItems(Paths.get("foo"), Paths.get("bar")));
|
MatcherAssert.assertThat(paths, CoreMatchers.hasItems(Paths.get("foo"), Paths.get("bar")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("./cryptomator.exe foo (with 'foo' being an invalid path)")
|
@DisplayName("./cryptomator.exe foo (with 'foo' being an invalid path)")
|
||||||
public void testOpenArgsWithIncorrectPaths() throws IOException {
|
public void testOpenArgsWithIncorrectPaths() {
|
||||||
FileSystem fs = Mockito.mock(FileSystem.class);
|
FileSystem fs = Mockito.mock(FileSystem.class);
|
||||||
Mockito.when(fs.getPath("foo")).thenThrow(new InvalidPathException("foo", "foo is not a path"));
|
Mockito.when(fs.getPath("foo")).thenThrow(new InvalidPathException("foo", "foo is not a path"));
|
||||||
inTest.handleLaunchArgs(fs, new String[]{"foo"});
|
inTest.handleLaunchArgs(fs, new String[]{"foo"});
|
||||||
|
|
||||||
AppLaunchEvent evt = queue.poll();
|
AppLaunchEvent evt = queue.poll();
|
||||||
Assertions.assertNotNull(evt);
|
Assertions.assertNull(evt);
|
||||||
List<Path> paths = evt.getPathsToOpen().collect(Collectors.toList());
|
|
||||||
Assertions.assertTrue(paths.isEmpty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("./cryptomator.exe foo (with full event queue)")
|
@DisplayName("./cryptomator.exe foo (with full event queue)")
|
||||||
public void testOpenArgsWithFullQueue() throws IOException {
|
public void testOpenArgsWithFullQueue() {
|
||||||
queue.add(new AppLaunchEvent(AppLaunchEvent.EventType.OPEN_FILE, Stream.empty()));
|
queue.add(new AppLaunchEvent(AppLaunchEvent.EventType.OPEN_FILE, Collections.emptyList()));
|
||||||
Assumptions.assumeTrue(queue.remainingCapacity() == 0);
|
Assumptions.assumeTrue(queue.remainingCapacity() == 0);
|
||||||
|
|
||||||
inTest.handleLaunchArgs(new String[]{"foo"});
|
inTest.handleLaunchArgs(new String[]{"foo"});
|
||||||
|
|||||||
+8
-8
@@ -3,13 +3,13 @@
|
|||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.5.0-beta2</version>
|
<version>1.5.0-beta3</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>Cryptomator</name>
|
<name>Cryptomator</name>
|
||||||
|
|
||||||
<organization>
|
<organization>
|
||||||
<name>cryptomator.org</name>
|
<name>cryptomator.org</name>
|
||||||
<url>http://cryptomator.org</url>
|
<url>https://cryptomator.org</url>
|
||||||
</organization>
|
</organization>
|
||||||
|
|
||||||
<developers>
|
<developers>
|
||||||
@@ -24,33 +24,33 @@
|
|||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|
||||||
<!-- cryptomator dependencies -->
|
<!-- cryptomator dependencies -->
|
||||||
<cryptomator.cryptofs.version>1.9.0-rc2</cryptomator.cryptofs.version>
|
<cryptomator.cryptofs.version>1.9.3</cryptomator.cryptofs.version>
|
||||||
<cryptomator.jni.version>2.2.2</cryptomator.jni.version>
|
<cryptomator.jni.version>2.2.2</cryptomator.jni.version>
|
||||||
<cryptomator.fuse.version>1.2.2</cryptomator.fuse.version>
|
<cryptomator.fuse.version>1.2.2</cryptomator.fuse.version>
|
||||||
<cryptomator.dokany.version>1.1.12</cryptomator.dokany.version>
|
<cryptomator.dokany.version>1.1.12</cryptomator.dokany.version>
|
||||||
<cryptomator.webdav.version>1.0.10</cryptomator.webdav.version>
|
<cryptomator.webdav.version>1.0.10</cryptomator.webdav.version>
|
||||||
|
|
||||||
<!-- 3rd party dependencies -->
|
<!-- 3rd party dependencies -->
|
||||||
<javafx.version>13.0.1</javafx.version>
|
<javafx.version>14-ea+8</javafx.version>
|
||||||
<commons-lang3.version>3.9</commons-lang3.version>
|
<commons-lang3.version>3.9</commons-lang3.version>
|
||||||
<jwt.version>3.8.3</jwt.version>
|
<jwt.version>3.8.3</jwt.version>
|
||||||
<easybind.version>1.0.3</easybind.version>
|
<easybind.version>1.0.3</easybind.version>
|
||||||
<guava.version>28.1-jre</guava.version>
|
<guava.version>28.1-jre</guava.version>
|
||||||
<dagger.version>2.25.2</dagger.version>
|
<dagger.version>2.26</dagger.version>
|
||||||
<gson.version>2.8.6</gson.version>
|
<gson.version>2.8.6</gson.version>
|
||||||
<slf4j.version>1.7.29</slf4j.version>
|
<slf4j.version>1.7.29</slf4j.version>
|
||||||
<logback.version>1.2.3</logback.version>
|
<logback.version>1.2.3</logback.version>
|
||||||
|
|
||||||
<!-- test dependencies -->
|
<!-- test dependencies -->
|
||||||
<junit.jupiter.version>5.5.2</junit.jupiter.version>
|
<junit.jupiter.version>5.6.0</junit.jupiter.version>
|
||||||
<mockito.version>3.1.0</mockito.version>
|
<mockito.version>3.2.4</mockito.version>
|
||||||
<hamcrest.version>2.2</hamcrest.version>
|
<hamcrest.version>2.2</hamcrest.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<repositories>
|
<repositories>
|
||||||
<repository>
|
<repository>
|
||||||
<id>jcenter</id>
|
<id>jcenter</id>
|
||||||
<url>http://jcenter.bintray.com</url>
|
<url>https://jcenter.bintray.com</url>
|
||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.cryptomator</groupId>
|
<groupId>org.cryptomator</groupId>
|
||||||
<artifactId>main</artifactId>
|
<artifactId>main</artifactId>
|
||||||
<version>1.5.0-beta2</version>
|
<version>1.5.0-beta3</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>ui</artifactId>
|
<artifactId>ui</artifactId>
|
||||||
<name>Cryptomator GUI</name>
|
<name>Cryptomator GUI</name>
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ import org.cryptomator.ui.recoverykey.RecoveryKeyDisplayController;
|
|||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@@ -51,13 +51,13 @@ public abstract class AddVaultModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@AddVaultWizardWindow
|
@AddVaultWizardWindow
|
||||||
@AddVaultWizardScoped
|
@AddVaultWizardScoped
|
||||||
static Stage provideStage(@MainWindow Stage owner, ResourceBundle resourceBundle, @Named("windowIcon") Optional<Image> windowIcon) {
|
static Stage provideStage(@MainWindow Stage owner, ResourceBundle resourceBundle, @Named("windowIcons") List<Image> windowIcons) {
|
||||||
Stage stage = new Stage();
|
Stage stage = new Stage();
|
||||||
stage.setTitle(resourceBundle.getString("addvaultwizard.title"));
|
stage.setTitle(resourceBundle.getString("addvaultwizard.title"));
|
||||||
stage.setResizable(false);
|
stage.setResizable(false);
|
||||||
stage.initModality(Modality.WINDOW_MODAL);
|
stage.initModality(Modality.WINDOW_MODAL);
|
||||||
stage.initOwner(owner);
|
stage.initOwner(owner);
|
||||||
windowIcon.ifPresent(stage.getIcons()::add);
|
stage.getIcons().addAll(windowIcons);
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,8 +193,8 @@ public abstract class AddVaultModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FxControllerKey(RecoveryKeyDisplayController.class)
|
@FxControllerKey(RecoveryKeyDisplayController.class)
|
||||||
static FxController provideRecoveryKeyDisplayController(@AddVaultWizardWindow Stage window, @Named("vaultName") StringProperty vaultName, @Named("recoveryKey") StringProperty recoveryKey) {
|
static FxController provideRecoveryKeyDisplayController(@AddVaultWizardWindow Stage window, @Named("vaultName") StringProperty vaultName, @Named("recoveryKey") StringProperty recoveryKey, ResourceBundle localization) {
|
||||||
return new RecoveryKeyDisplayController(window, vaultName.get(), recoveryKey.get());
|
return new RecoveryKeyDisplayController(window, vaultName.get(), recoveryKey.get(), localization);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ public class ReadmeGenerator {
|
|||||||
// specs: https://web.archive.org/web/20190708132914/http://www.kleinlercher.at/tools/Windows_Protocols/Word2007RTFSpec9.pdf
|
// specs: https://web.archive.org/web/20190708132914/http://www.kleinlercher.at/tools/Windows_Protocols/Word2007RTFSpec9.pdf
|
||||||
private static final String RTF_HEADER = "{\\rtf1\\fbidis\\ansi\\uc0\\fs32\n";
|
private static final String RTF_HEADER = "{\\rtf1\\fbidis\\ansi\\uc0\\fs32\n";
|
||||||
private static final String RTF_FOOTER = "}";
|
private static final String RTF_FOOTER = "}";
|
||||||
private static final String HELP_URL = "{\\field{\\*\\fldinst HYPERLINK \"http://www.google.com/\"}{\\fldrslt google.com}}";
|
private static final String HELP_URL = "{\\field{\\*\\fldinst HYPERLINK \"http://docs.cryptoamtor.org/\"}{\\fldrslt docs.cryptoamtor.org}}";
|
||||||
|
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ import org.cryptomator.ui.common.PasswordStrengthUtil;
|
|||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
import java.nio.CharBuffer;
|
import java.nio.CharBuffer;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@@ -46,13 +46,13 @@ abstract class ChangePasswordModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@ChangePasswordWindow
|
@ChangePasswordWindow
|
||||||
@ChangePasswordScoped
|
@ChangePasswordScoped
|
||||||
static Stage provideStage(@Named("changePasswordOwner") Stage owner, ResourceBundle resourceBundle, @Named("windowIcon") Optional<Image> windowIcon) {
|
static Stage provideStage(@Named("changePasswordOwner") Stage owner, ResourceBundle resourceBundle, @Named("windowIcons") List<Image> windowIcons) {
|
||||||
Stage stage = new Stage();
|
Stage stage = new Stage();
|
||||||
stage.setTitle(resourceBundle.getString("changepassword.title"));
|
stage.setTitle(resourceBundle.getString("changepassword.title"));
|
||||||
stage.setResizable(false);
|
stage.setResizable(false);
|
||||||
stage.initModality(Modality.WINDOW_MODAL);
|
stage.initModality(Modality.WINDOW_MODAL);
|
||||||
stage.initOwner(owner);
|
stage.initOwner(owner);
|
||||||
windowIcon.ifPresent(stage.getIcons()::add);
|
stage.getIcons().addAll(windowIcons);
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package org.cryptomator.ui.common;
|
||||||
|
|
||||||
|
import javafx.animation.KeyFrame;
|
||||||
|
import javafx.animation.KeyValue;
|
||||||
|
import javafx.animation.Timeline;
|
||||||
|
import javafx.beans.value.WritableValue;
|
||||||
|
import javafx.stage.Window;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
|
||||||
|
public class Animations {
|
||||||
|
|
||||||
|
public static Timeline createShakeWindowAnimation(Window window) {
|
||||||
|
WritableValue<Double> writableWindowX = new WritableValue<>() {
|
||||||
|
@Override
|
||||||
|
public Double getValue() {
|
||||||
|
return window.getX();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setValue(Double value) {
|
||||||
|
window.setX(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return new Timeline( //
|
||||||
|
new KeyFrame(Duration.ZERO, new KeyValue(writableWindowX, window.getX())), //
|
||||||
|
new KeyFrame(new Duration(100), new KeyValue(writableWindowX, window.getX() - 22.0)), //
|
||||||
|
new KeyFrame(new Duration(200), new KeyValue(writableWindowX, window.getX() + 18.0)), //
|
||||||
|
new KeyFrame(new Duration(300), new KeyValue(writableWindowX, window.getX() - 14.0)), //
|
||||||
|
new KeyFrame(new Duration(400), new KeyValue(writableWindowX, window.getX() + 10.0)), //
|
||||||
|
new KeyFrame(new Duration(500), new KeyValue(writableWindowX, window.getX() - 6.0)), //
|
||||||
|
new KeyFrame(new Duration(600), new KeyValue(writableWindowX, window.getX() + 2.0)), //
|
||||||
|
new KeyFrame(new Duration(700), new KeyValue(writableWindowX, window.getX())) //
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -12,12 +12,16 @@ public enum FxmlFile {
|
|||||||
CHANGEPASSWORD("/fxml/changepassword.fxml"), //
|
CHANGEPASSWORD("/fxml/changepassword.fxml"), //
|
||||||
FORGET_PASSWORD("/fxml/forget_password.fxml"), //
|
FORGET_PASSWORD("/fxml/forget_password.fxml"), //
|
||||||
MAIN_WINDOW("/fxml/main_window.fxml"), //
|
MAIN_WINDOW("/fxml/main_window.fxml"), //
|
||||||
|
MIGRATION_CAPABILITY_ERROR("/fxml/migration_capability_error.fxml"), //
|
||||||
|
MIGRATION_GENERIC_ERROR("/fxml/migration_generic_error.fxml"), //
|
||||||
MIGRATION_RUN("/fxml/migration_run.fxml"), //
|
MIGRATION_RUN("/fxml/migration_run.fxml"), //
|
||||||
MIGRATION_START("/fxml/migration_start.fxml"), //
|
MIGRATION_START("/fxml/migration_start.fxml"), //
|
||||||
MIGRATION_SUCCESS("/fxml/migration_success.fxml"), //
|
MIGRATION_SUCCESS("/fxml/migration_success.fxml"), //
|
||||||
PREFERENCES("/fxml/preferences.fxml"), //
|
PREFERENCES("/fxml/preferences.fxml"), //
|
||||||
QUIT("/fxml/quit.fxml"), //
|
QUIT("/fxml/quit.fxml"), //
|
||||||
RECOVERYKEY_CREATE("/fxml/recoverykey_create.fxml"), //
|
RECOVERYKEY_CREATE("/fxml/recoverykey_create.fxml"), //
|
||||||
|
RECOVERYKEY_RECOVER("/fxml/recoverykey_recover.fxml"), //
|
||||||
|
RECOVERYKEY_RESET_PASSWORD("/fxml/recoverykey_reset_password.fxml"), //
|
||||||
RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
|
RECOVERYKEY_SUCCESS("/fxml/recoverykey_success.fxml"), //
|
||||||
REMOVE_VAULT("/fxml/remove_vault.fxml"), //
|
REMOVE_VAULT("/fxml/remove_vault.fxml"), //
|
||||||
UNLOCK("/fxml/unlock.fxml"),
|
UNLOCK("/fxml/unlock.fxml"),
|
||||||
|
|||||||
@@ -8,11 +8,11 @@ public class StackTraceController implements FxController {
|
|||||||
|
|
||||||
private final String stackTrace;
|
private final String stackTrace;
|
||||||
|
|
||||||
public StackTraceController(Exception cause) {
|
public StackTraceController(Throwable cause) {
|
||||||
this.stackTrace = provideStackTrace(cause);
|
this.stackTrace = provideStackTrace(cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
static String provideStackTrace(Exception cause) {
|
private static String provideStackTrace(Throwable cause) {
|
||||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
cause.printStackTrace(new PrintStream(baos));
|
cause.printStackTrace(new PrintStream(baos));
|
||||||
return baos.toString(StandardCharsets.UTF_8);
|
return baos.toString(StandardCharsets.UTF_8);
|
||||||
|
|||||||
@@ -73,21 +73,21 @@ public class Tasks {
|
|||||||
return new TaskImpl<>(callable, successHandler, errorHandlers, finallyHandler);
|
return new TaskImpl<>(callable, successHandler, errorHandlers, finallyHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<T> runOnce(ExecutorService executorService) {
|
public Task<T> runOnce(ExecutorService executor) {
|
||||||
Task<T> task = build();
|
Task<T> task = build();
|
||||||
executorService.submit(task);
|
executor.submit(task);
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<T> scheduleOnce(ScheduledExecutorService executorService, long delay, TimeUnit unit) {
|
public Task<T> scheduleOnce(ScheduledExecutorService scheduler, long delay, TimeUnit unit) {
|
||||||
Task<T> task = build();
|
Task<T> task = build();
|
||||||
executorService.schedule(task, delay, unit);
|
scheduler.schedule(task, delay, unit);
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScheduledService<T> schedulePeriodically(ExecutorService executorService, Duration initialDelay, Duration period) {
|
public ScheduledService<T> schedulePeriodically(ExecutorService executor, Duration initialDelay, Duration period) {
|
||||||
ScheduledService<T> service = new RestartingService<>(this::build);
|
ScheduledService<T> service = new RestartingService<>(this::build);
|
||||||
service.setExecutor(executorService);
|
service.setExecutor(executor);
|
||||||
service.setDelay(initialDelay);
|
service.setDelay(initialDelay);
|
||||||
service.setPeriod(period);
|
service.setPeriod(period);
|
||||||
Platform.runLater(service::start);
|
Platform.runLater(service::start);
|
||||||
|
|||||||
@@ -4,15 +4,20 @@ import javafx.concurrent.Task;
|
|||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultState;
|
import org.cryptomator.common.vaults.VaultState;
|
||||||
import org.cryptomator.common.vaults.Volume;
|
import org.cryptomator.common.vaults.Volume;
|
||||||
|
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||||
|
import org.cryptomator.keychain.KeychainAccess;
|
||||||
import org.cryptomator.ui.fxapp.FxApplicationScoped;
|
import org.cryptomator.ui.fxapp.FxApplicationScoped;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -23,10 +28,12 @@ public class VaultService {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(VaultService.class);
|
private static final Logger LOG = LoggerFactory.getLogger(VaultService.class);
|
||||||
|
|
||||||
private final ExecutorService executorService;
|
private final ExecutorService executorService;
|
||||||
|
private final Optional<KeychainAccess> keychain;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public VaultService(ExecutorService executorService) {
|
public VaultService(ExecutorService executorService, Optional<KeychainAccess> keychain) {
|
||||||
this.executorService = executorService;
|
this.executorService = executorService;
|
||||||
|
this.keychain = keychain;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reveal(Vault vault) {
|
public void reveal(Vault vault) {
|
||||||
@@ -45,6 +52,72 @@ public class VaultService {
|
|||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to unlock all given vaults in a background thread using passwords stored in the system keychain.
|
||||||
|
*
|
||||||
|
* @param vaults The vaults to unlock
|
||||||
|
* @implNote No-op if no system keychain is present
|
||||||
|
*/
|
||||||
|
public void attemptAutoUnlock(Collection<Vault> vaults) {
|
||||||
|
if (!keychain.isPresent()) {
|
||||||
|
LOG.debug("No system keychain found. Unable to auto unlock without saved passwords.");
|
||||||
|
} else {
|
||||||
|
for (Vault vault : vaults) {
|
||||||
|
attemptAutoUnlock(vault, keychain.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlocks a vault in a background thread using a stored passphrase
|
||||||
|
*
|
||||||
|
* @param vault The vault to unlock
|
||||||
|
* @param keychainAccess The system keychain holding the passphrase for the vault
|
||||||
|
*/
|
||||||
|
public void attemptAutoUnlock(Vault vault, KeychainAccess keychainAccess) {
|
||||||
|
executorService.execute(createAutoUnlockTask(vault, keychainAccess));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates but doesn't start an auto-unlock task.
|
||||||
|
*
|
||||||
|
* @param vault The vault to unlock
|
||||||
|
* @param keychainAccess The system keychain holding the passphrase for the vault
|
||||||
|
* @return The task
|
||||||
|
*/
|
||||||
|
public Task<Vault> createAutoUnlockTask(Vault vault, KeychainAccess keychainAccess) {
|
||||||
|
Task<Vault> task = new AutoUnlockVaultTask(vault, keychainAccess);
|
||||||
|
task.setOnSucceeded(evt -> LOG.info("Auto-unlocked {}", vault.getDisplayableName()));
|
||||||
|
task.setOnFailed(evt -> LOG.error("Failed to auto-unlock " + vault.getDisplayableName(), evt.getSource().getException()));
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlocks a vault in a background thread
|
||||||
|
*
|
||||||
|
* @param vault The vault to unlock
|
||||||
|
* @param passphrase The password to use - wipe this param asap
|
||||||
|
* @implNote A copy of the passphrase will be made, which is wiped as soon as the task ran.
|
||||||
|
*/
|
||||||
|
public void unlock(Vault vault, CharSequence passphrase) {
|
||||||
|
executorService.execute(createUnlockTask(vault, passphrase));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates but doesn't start an unlock task.
|
||||||
|
*
|
||||||
|
* @param vault The vault to unlock
|
||||||
|
* @param passphrase The password to use - wipe this param asap
|
||||||
|
* @return The task
|
||||||
|
* @implNote A copy of the passphrase will be made, which is wiped as soon as the task ran.
|
||||||
|
*/
|
||||||
|
public Task<Vault> createUnlockTask(Vault vault, CharSequence passphrase) {
|
||||||
|
Task<Vault> task = new UnlockVaultTask(vault, passphrase);
|
||||||
|
task.setOnSucceeded(evt -> LOG.info("Unlocked {}", vault.getDisplayableName()));
|
||||||
|
task.setOnFailed(evt -> LOG.error("Failed to unlock " + vault.getDisplayableName(), evt.getSource().getException()));
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Locks a vault in a background thread.
|
* Locks a vault in a background thread.
|
||||||
*
|
*
|
||||||
@@ -60,6 +133,7 @@ public class VaultService {
|
|||||||
*
|
*
|
||||||
* @param vault The vault to lock
|
* @param vault The vault to lock
|
||||||
* @param forced Whether to attempt a forced lock
|
* @param forced Whether to attempt a forced lock
|
||||||
|
* @return The task
|
||||||
*/
|
*/
|
||||||
public Task<Vault> createLockTask(Vault vault, boolean forced) {
|
public Task<Vault> createLockTask(Vault vault, boolean forced) {
|
||||||
Task<Vault> task = new LockVaultTask(vault, forced);
|
Task<Vault> task = new LockVaultTask(vault, forced);
|
||||||
@@ -145,6 +219,93 @@ public class VaultService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class AutoUnlockVaultTask extends Task<Vault> {
|
||||||
|
|
||||||
|
private final Vault vault;
|
||||||
|
private final KeychainAccess keychain;
|
||||||
|
|
||||||
|
public AutoUnlockVaultTask(Vault vault, KeychainAccess keychain) {
|
||||||
|
this.vault = vault;
|
||||||
|
this.keychain = keychain;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Vault call() throws Exception {
|
||||||
|
char[] storedPw = null;
|
||||||
|
try {
|
||||||
|
storedPw = keychain.loadPassphrase(vault.getId());
|
||||||
|
if (storedPw == null) {
|
||||||
|
throw new InvalidPassphraseException();
|
||||||
|
}
|
||||||
|
vault.unlock(CharBuffer.wrap(storedPw));
|
||||||
|
} finally {
|
||||||
|
if (storedPw != null) {
|
||||||
|
Arrays.fill(storedPw, ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vault;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void scheduled() {
|
||||||
|
vault.setState(VaultState.PROCESSING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void succeeded() {
|
||||||
|
vault.setState(VaultState.UNLOCKED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void failed() {
|
||||||
|
vault.setState(VaultState.LOCKED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class UnlockVaultTask extends Task<Vault> {
|
||||||
|
|
||||||
|
private final Vault vault;
|
||||||
|
private final CharBuffer passphrase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param vault The vault to unlock
|
||||||
|
* @param passphrase The password to use - wipe this param asap
|
||||||
|
* @implNote A copy of the passphrase will be made, which is wiped as soon as the task ran.
|
||||||
|
*/
|
||||||
|
public UnlockVaultTask(Vault vault, CharSequence passphrase) {
|
||||||
|
this.vault = vault;
|
||||||
|
this.passphrase = CharBuffer.allocate(passphrase.length());
|
||||||
|
for (int i = 0; i < passphrase.length(); i++) {
|
||||||
|
this.passphrase.put(i, passphrase.charAt(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Vault call() throws Exception {
|
||||||
|
try {
|
||||||
|
vault.unlock(passphrase);
|
||||||
|
} finally {
|
||||||
|
Arrays.fill(passphrase.array(), ' ');
|
||||||
|
}
|
||||||
|
return vault;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void scheduled() {
|
||||||
|
vault.setState(VaultState.PROCESSING);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void succeeded() {
|
||||||
|
vault.setState(VaultState.UNLOCKED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void failed() {
|
||||||
|
vault.setState(VaultState.LOCKED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A task that locks a vault
|
* A task that locks a vault
|
||||||
*/
|
*/
|
||||||
@@ -186,5 +347,4 @@ public class VaultService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ package org.cryptomator.ui.controls;
|
|||||||
*/
|
*/
|
||||||
public enum FontAwesome5Icon {
|
public enum FontAwesome5Icon {
|
||||||
ANCHOR("\uF13D"), //
|
ANCHOR("\uF13D"), //
|
||||||
ARROW_ALT_UP("\uF357"), //
|
ARROW_UP("\uF062"), //
|
||||||
CHECK("\uF00C"), //
|
CHECK("\uF00C"), //
|
||||||
COG("\uF013"), //
|
COG("\uF013"), //
|
||||||
COGS("\uF085"), //
|
COGS("\uF085"), //
|
||||||
COPY("\uF0C5"), //
|
COPY("\uF0C5"), //
|
||||||
EXCLAMATION("\uF12A"),
|
CROWN("\uF521"), //
|
||||||
|
EXCLAMATION("\uF12A"), //
|
||||||
EXCLAMATION_CIRCLE("\uF06A"), //
|
EXCLAMATION_CIRCLE("\uF06A"), //
|
||||||
EXCLAMATION_TRIANGLE("\uF071"), //
|
EXCLAMATION_TRIANGLE("\uF071"), //
|
||||||
EYE("\uF06E"), //
|
EYE("\uF06E"), //
|
||||||
@@ -22,17 +23,17 @@ public enum FontAwesome5Icon {
|
|||||||
HDD("\uF0A0"), //
|
HDD("\uF0A0"), //
|
||||||
KEY("\uF084"), //
|
KEY("\uF084"), //
|
||||||
LINK("\uF0C1"), //
|
LINK("\uF0C1"), //
|
||||||
LOCK_ALT("\uF30D"), //
|
LOCK("\uF023"), //
|
||||||
LOCK_OPEN_ALT("\uF3C2"), //
|
LOCK_OPEN("\uF3C1"), //
|
||||||
|
MAGIC("\uF0D0"), //
|
||||||
PLUS("\uF067"), //
|
PLUS("\uF067"), //
|
||||||
PRINT("\uF02F"), //
|
PRINT("\uF02F"), //
|
||||||
QUESTION("\uF128"), //
|
QUESTION("\uF128"), //
|
||||||
SPARKLES("\uF890"), //
|
|
||||||
SPINNER("\uF110"), //
|
SPINNER("\uF110"), //
|
||||||
SYNC("\uF021"), //
|
SYNC("\uF021"), //
|
||||||
TIMES("\uF00D"), //
|
TIMES("\uF00D"), //
|
||||||
USER_CROWN("\uF6A4"), //
|
|
||||||
WRENCH("\uF0AD"), //
|
WRENCH("\uF0AD"), //
|
||||||
|
WINDOW_MINIMIZE("\uF2D1"), //
|
||||||
;
|
;
|
||||||
|
|
||||||
private final String unicode;
|
private final String unicode;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public class FontAwesome5IconView extends Text {
|
|||||||
|
|
||||||
private static final FontAwesome5Icon DEFAULT_GLYPH = FontAwesome5Icon.ANCHOR;
|
private static final FontAwesome5Icon DEFAULT_GLYPH = FontAwesome5Icon.ANCHOR;
|
||||||
private static final double DEFAULT_GLYPH_SIZE = 12.0;
|
private static final double DEFAULT_GLYPH_SIZE = 12.0;
|
||||||
private static final String FONT_PATH = "/css/fontawesome5-pro-solid.otf";
|
private static final String FONT_PATH = "/css/fontawesome5-free-solid.otf";
|
||||||
private static final Font FONT;
|
private static final Font FONT;
|
||||||
|
|
||||||
private ObjectProperty<FontAwesome5Icon> glyph = new SimpleObjectProperty<>(this, "glyph", DEFAULT_GLYPH);
|
private ObjectProperty<FontAwesome5Icon> glyph = new SimpleObjectProperty<>(this, "glyph", DEFAULT_GLYPH);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public class NiceSecurePasswordField extends StackPane {
|
|||||||
iconContainer.getStyleClass().add(ICONS_STLYE_CLASS);
|
iconContainer.getStyleClass().add(ICONS_STLYE_CLASS);
|
||||||
StackPane.setAlignment(iconContainer, Pos.CENTER_RIGHT);
|
StackPane.setAlignment(iconContainer, Pos.CENTER_RIGHT);
|
||||||
|
|
||||||
capsLockedIcon.setGlyph(FontAwesome5Icon.ARROW_ALT_UP);
|
capsLockedIcon.setGlyph(FontAwesome5Icon.ARROW_UP);
|
||||||
capsLockedIcon.setGlyphSize(ICON_SIZE);
|
capsLockedIcon.setGlyphSize(ICON_SIZE);
|
||||||
capsLockedIcon.visibleProperty().bind(passwordField.capsLockedProperty());
|
capsLockedIcon.visibleProperty().bind(passwordField.capsLockedProperty());
|
||||||
capsLockedIcon.managedProperty().bind(passwordField.capsLockedProperty());
|
capsLockedIcon.managedProperty().bind(passwordField.capsLockedProperty());
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import org.cryptomator.ui.common.FxmlScene;
|
|||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@@ -37,13 +37,13 @@ abstract class ForgetPasswordModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@ForgetPasswordWindow
|
@ForgetPasswordWindow
|
||||||
@ForgetPasswordScoped
|
@ForgetPasswordScoped
|
||||||
static Stage provideStage(ResourceBundle resourceBundle, @Named("windowIcon") Optional<Image> windowIcon, @Named("forgetPasswordOwner") Stage owner) {
|
static Stage provideStage(ResourceBundle resourceBundle, @Named("windowIcons") List<Image> windowIcons, @Named("forgetPasswordOwner") Stage owner) {
|
||||||
Stage stage = new Stage();
|
Stage stage = new Stage();
|
||||||
stage.setTitle(resourceBundle.getString("forgetPassword.title"));
|
stage.setTitle(resourceBundle.getString("forgetPassword.title"));
|
||||||
stage.setResizable(false);
|
stage.setResizable(false);
|
||||||
stage.initModality(Modality.WINDOW_MODAL);
|
stage.initModality(Modality.WINDOW_MODAL);
|
||||||
stage.initOwner(owner);
|
stage.initOwner(owner);
|
||||||
windowIcon.ifPresent(stage.getIcons()::add);
|
stage.getIcons().addAll(windowIcons);
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ import org.cryptomator.ui.unlock.UnlockComponent;
|
|||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Optional;
|
import java.io.UncheckedIOException;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, QuitComponent.class})
|
@Module(includes = {UpdateCheckerModule.class}, subcomponents = {MainWindowComponent.class, PreferencesComponent.class, UnlockComponent.class, QuitComponent.class})
|
||||||
abstract class FxApplicationModule {
|
abstract class FxApplicationModule {
|
||||||
@@ -34,19 +36,29 @@ abstract class FxApplicationModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Named("windowIcon")
|
@Named("windowIcons")
|
||||||
@FxApplicationScoped
|
@FxApplicationScoped
|
||||||
static Optional<Image> provideWindowIcon() {
|
static List<Image> provideWindowIcons() {
|
||||||
if (SystemUtils.IS_OS_MAC) {
|
if (SystemUtils.IS_OS_MAC) {
|
||||||
return Optional.empty();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
try (InputStream in = FxApplicationModule.class.getResourceAsStream("/window_icon_32.png")) { // TODO: use some higher res depending on display?
|
|
||||||
return Optional.of(new Image(in));
|
try {
|
||||||
|
return List.of( //
|
||||||
|
createImageFromResource("/window_icon_32.png"), //
|
||||||
|
createImageFromResource("/window_icon_512.png") //
|
||||||
|
);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return Optional.empty();
|
throw new UncheckedIOException("Failed to load embedded resource.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Image createImageFromResource(String resourceName) throws IOException {
|
||||||
|
try (InputStream in = FxApplicationModule.class.getResourceAsStream(resourceName)) {
|
||||||
|
return new Image(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract Application bindApplication(FxApplication application);
|
abstract Application bindApplication(FxApplication application);
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
package org.cryptomator.ui.launcher;
|
package org.cryptomator.ui.launcher;
|
||||||
|
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class AppLaunchEvent {
|
public class AppLaunchEvent {
|
||||||
|
|
||||||
private final Stream<Path> pathsToOpen;
|
|
||||||
private final EventType type;
|
private final EventType type;
|
||||||
|
private final Collection<Path> pathsToOpen;
|
||||||
|
|
||||||
public enum EventType {REVEAL_APP, OPEN_FILE}
|
public enum EventType {REVEAL_APP, OPEN_FILE}
|
||||||
|
|
||||||
public AppLaunchEvent(EventType type, Stream<Path> pathsToOpen) {
|
public AppLaunchEvent(EventType type, Collection<Path> pathsToOpen) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.pathsToOpen = pathsToOpen;
|
this.pathsToOpen = pathsToOpen;
|
||||||
}
|
}
|
||||||
@@ -19,7 +20,7 @@ public class AppLaunchEvent {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream<Path> getPathsToOpen() {
|
public Collection<Path> getPathsToOpen() {
|
||||||
return pathsToOpen;
|
return pathsToOpen;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
package org.cryptomator.ui.launcher;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.Observable;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import org.cryptomator.common.ShutdownHook;
|
||||||
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.common.vaults.VaultState;
|
||||||
|
import org.cryptomator.common.vaults.Volume;
|
||||||
|
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import javax.inject.Singleton;
|
||||||
|
import java.awt.Desktop;
|
||||||
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.desktop.QuitResponse;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.EventObject;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
@Singleton
|
||||||
|
public class AppLifecycleListener {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(AppLifecycleListener.class);
|
||||||
|
public static final Set<VaultState> STATES_ALLOWING_TERMINATION = EnumSet.of(VaultState.LOCKED, VaultState.NEEDS_MIGRATION, VaultState.MISSING, VaultState.ERROR);
|
||||||
|
|
||||||
|
private final FxApplicationStarter fxApplicationStarter;
|
||||||
|
private final CountDownLatch shutdownLatch;
|
||||||
|
private final ObservableList<Vault> vaults;
|
||||||
|
private final AtomicBoolean allowQuitWithoutPrompt;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
AppLifecycleListener(FxApplicationStarter fxApplicationStarter, @Named("shutdownLatch") CountDownLatch shutdownLatch, ShutdownHook shutdownHook, ObservableList<Vault> vaults) {
|
||||||
|
this.fxApplicationStarter = fxApplicationStarter;
|
||||||
|
this.shutdownLatch = shutdownLatch;
|
||||||
|
this.vaults = vaults;
|
||||||
|
this.allowQuitWithoutPrompt = new AtomicBoolean(true);
|
||||||
|
vaults.addListener(this::vaultListChanged);
|
||||||
|
|
||||||
|
// register preferences shortcut
|
||||||
|
if (Desktop.getDesktop().isSupported(Desktop.Action.APP_PREFERENCES)) {
|
||||||
|
Desktop.getDesktop().setPreferencesHandler(this::showPreferencesWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
// register quit handler
|
||||||
|
if (Desktop.getDesktop().isSupported(Desktop.Action.APP_QUIT_HANDLER)) {
|
||||||
|
Desktop.getDesktop().setQuitHandler(this::handleQuitRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
shutdownHook.runOnShutdown(this::forceUnmountRemainingVaults);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gracefully terminates the application.
|
||||||
|
*/
|
||||||
|
public void quit() {
|
||||||
|
handleQuitRequest(null, new QuitResponse() {
|
||||||
|
@Override
|
||||||
|
public void performQuit() {
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelQuit() {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleQuitRequest(@SuppressWarnings("unused") EventObject e, QuitResponse response) {
|
||||||
|
QuitResponse decoratedQuitResponse = decorateQuitResponse(response);
|
||||||
|
if (allowQuitWithoutPrompt.get()) {
|
||||||
|
decoratedQuitResponse.performQuit();
|
||||||
|
} else {
|
||||||
|
fxApplicationStarter.get(true).thenAccept(app -> app.showQuitWindow(decoratedQuitResponse));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private QuitResponse decorateQuitResponse(QuitResponse originalQuitResponse) {
|
||||||
|
return new QuitResponse() {
|
||||||
|
@Override
|
||||||
|
public void performQuit() {
|
||||||
|
Platform.exit(); // will be no-op, if JavaFX never started.
|
||||||
|
shutdownLatch.countDown(); // main thread is waiting for this latch
|
||||||
|
EventQueue.invokeLater(originalQuitResponse::performQuit); // this will eventually call System.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelQuit() {
|
||||||
|
originalQuitResponse.cancelQuit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void vaultListChanged(@SuppressWarnings("unused") Observable observable) {
|
||||||
|
assert Platform.isFxApplicationThread();
|
||||||
|
boolean allVaultsAllowTermination = vaults.stream().map(Vault::getState).allMatch(STATES_ALLOWING_TERMINATION::contains);
|
||||||
|
boolean suddenTerminationChanged = allowQuitWithoutPrompt.compareAndSet(!allVaultsAllowTermination, allVaultsAllowTermination);
|
||||||
|
if (suddenTerminationChanged) {
|
||||||
|
LOG.debug("Allow quitting without prompt: {}", allVaultsAllowTermination);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showPreferencesWindow(@SuppressWarnings("unused") EventObject actionEvent) {
|
||||||
|
fxApplicationStarter.get(true).thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void forceUnmountRemainingVaults() {
|
||||||
|
for (Vault vault : vaults) {
|
||||||
|
if (vault.isUnlocked()) {
|
||||||
|
try {
|
||||||
|
vault.lock(true);
|
||||||
|
} catch (Volume.VolumeException e) {
|
||||||
|
LOG.error("Failed to unmount vault " + vault.getPath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package org.cryptomator.ui.launcher;
|
package org.cryptomator.ui.launcher;
|
||||||
|
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import org.cryptomator.common.settings.Settings;
|
import org.cryptomator.common.settings.Settings;
|
||||||
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.jni.JniException;
|
import org.cryptomator.jni.JniException;
|
||||||
import org.cryptomator.jni.MacApplicationUiState;
|
import org.cryptomator.jni.MacApplicationUiState;
|
||||||
import org.cryptomator.jni.MacFunctions;
|
import org.cryptomator.jni.MacFunctions;
|
||||||
@@ -13,9 +15,8 @@ import javax.inject.Inject;
|
|||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import java.awt.Desktop;
|
import java.awt.Desktop;
|
||||||
import java.awt.SystemTray;
|
import java.awt.SystemTray;
|
||||||
import java.awt.desktop.AppReopenedEvent;
|
|
||||||
import java.awt.desktop.AppReopenedListener;
|
import java.awt.desktop.AppReopenedListener;
|
||||||
import java.awt.desktop.SystemEventListener;
|
import java.util.Collection;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@@ -24,14 +25,16 @@ public class UiLauncher {
|
|||||||
private static final Logger LOG = LoggerFactory.getLogger(UiLauncher.class);
|
private static final Logger LOG = LoggerFactory.getLogger(UiLauncher.class);
|
||||||
|
|
||||||
private final Settings settings;
|
private final Settings settings;
|
||||||
|
private final ObservableList<Vault> vaults;
|
||||||
private final TrayMenuComponent.Builder trayComponent;
|
private final TrayMenuComponent.Builder trayComponent;
|
||||||
private final FxApplicationStarter fxApplicationStarter;
|
private final FxApplicationStarter fxApplicationStarter;
|
||||||
private final AppLaunchEventHandler launchEventHandler;
|
private final AppLaunchEventHandler launchEventHandler;
|
||||||
private final Optional<MacFunctions> macFunctions;
|
private final Optional<MacFunctions> macFunctions;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UiLauncher(Settings settings, TrayMenuComponent.Builder trayComponent, FxApplicationStarter fxApplicationStarter, AppLaunchEventHandler launchEventHandler, Optional<MacFunctions> macFunctions) {
|
public UiLauncher(Settings settings, ObservableList<Vault> vaults, TrayMenuComponent.Builder trayComponent, FxApplicationStarter fxApplicationStarter, AppLaunchEventHandler launchEventHandler, Optional<MacFunctions> macFunctions) {
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
this.vaults = vaults;
|
||||||
this.trayComponent = trayComponent;
|
this.trayComponent = trayComponent;
|
||||||
this.fxApplicationStarter = fxApplicationStarter;
|
this.fxApplicationStarter = fxApplicationStarter;
|
||||||
this.launchEventHandler = launchEventHandler;
|
this.launchEventHandler = launchEventHandler;
|
||||||
@@ -48,7 +51,7 @@ public class UiLauncher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// show window on start?
|
// show window on start?
|
||||||
if (settings.startHidden().get()) {
|
if (hasTrayIcon && settings.startHidden().get()) {
|
||||||
LOG.debug("Hiding application...");
|
LOG.debug("Hiding application...");
|
||||||
macFunctions.map(MacFunctions::uiState).ifPresent(JniException.ignore(MacApplicationUiState::transformToAgentApplication));
|
macFunctions.map(MacFunctions::uiState).ifPresent(JniException.ignore(MacApplicationUiState::transformToAgentApplication));
|
||||||
} else {
|
} else {
|
||||||
@@ -58,6 +61,12 @@ public class UiLauncher {
|
|||||||
// register app reopen listener
|
// register app reopen listener
|
||||||
Desktop.getDesktop().addAppEventListener((AppReopenedListener) e -> showMainWindowAsync(hasTrayIcon));
|
Desktop.getDesktop().addAppEventListener((AppReopenedListener) e -> showMainWindowAsync(hasTrayIcon));
|
||||||
|
|
||||||
|
// auto unlock
|
||||||
|
Collection<Vault> vaultsWithAutoUnlockEnabled = vaults.filtered(v -> v.getVaultSettings().unlockAfterStartup().get());
|
||||||
|
if (!vaultsWithAutoUnlockEnabled.isEmpty()) {
|
||||||
|
fxApplicationStarter.get(hasTrayIcon).thenAccept(app -> app.getVaultService().attemptAutoUnlock(vaultsWithAutoUnlockEnabled));
|
||||||
|
}
|
||||||
|
|
||||||
launchEventHandler.startHandlingLaunchEvents(hasTrayIcon);
|
launchEventHandler.startHandlingLaunchEvents(hasTrayIcon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
|
|||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@Module(subcomponents = {AddVaultWizardComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultOptionsComponent.class, WrongFileAlertComponent.class})
|
@Module(subcomponents = {AddVaultWizardComponent.class, MigrationComponent.class, RemoveVaultComponent.class, VaultOptionsComponent.class, WrongFileAlertComponent.class})
|
||||||
@@ -42,7 +42,7 @@ abstract class MainWindowModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@MainWindow
|
@MainWindow
|
||||||
@MainWindowScoped
|
@MainWindowScoped
|
||||||
static Stage provideStage(@Named("windowIcon") Optional<Image> windowIcon) {
|
static Stage provideStage(@Named("windowIcons") List<Image> windowIcons) {
|
||||||
Stage stage = new Stage(StageStyle.UNDECORATED);
|
Stage stage = new Stage(StageStyle.UNDECORATED);
|
||||||
// TODO: min/max values chosen arbitrarily. We might wanna take a look at the user's resolution...
|
// TODO: min/max values chosen arbitrarily. We might wanna take a look at the user's resolution...
|
||||||
stage.setMinWidth(650);
|
stage.setMinWidth(650);
|
||||||
@@ -50,7 +50,7 @@ abstract class MainWindowModule {
|
|||||||
stage.setMaxWidth(1000);
|
stage.setMaxWidth(1000);
|
||||||
stage.setMaxHeight(700);
|
stage.setMaxHeight(700);
|
||||||
stage.setTitle("Cryptomator");
|
stage.setTitle("Cryptomator");
|
||||||
windowIcon.ifPresent(stage.getIcons()::add);
|
stage.getIcons().addAll(windowIcons);
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,11 @@ import javafx.fxml.FXML;
|
|||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import org.cryptomator.common.LicenseHolder;
|
import org.cryptomator.common.LicenseHolder;
|
||||||
import org.cryptomator.common.vaults.VaultListManager;
|
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.fxapp.FxApplication;
|
import org.cryptomator.ui.fxapp.FxApplication;
|
||||||
import org.cryptomator.ui.fxapp.UpdateChecker;
|
import org.cryptomator.ui.fxapp.UpdateChecker;
|
||||||
|
import org.cryptomator.ui.launcher.AppLifecycleListener;
|
||||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||||
import org.cryptomator.ui.wrongfilealert.WrongFileAlertComponent;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -24,6 +23,7 @@ public class MainWindowTitleController implements FxController {
|
|||||||
|
|
||||||
public HBox titleBar;
|
public HBox titleBar;
|
||||||
|
|
||||||
|
private final AppLifecycleListener appLifecycle;
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final FxApplication application;
|
private final FxApplication application;
|
||||||
private final boolean minimizeToSysTray;
|
private final boolean minimizeToSysTray;
|
||||||
@@ -35,7 +35,8 @@ public class MainWindowTitleController implements FxController {
|
|||||||
private double yOffset;
|
private double yOffset;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
MainWindowTitleController(@MainWindow Stage window, FxApplication application, @Named("trayMenuSupported") boolean minimizeToSysTray, UpdateChecker updateChecker, LicenseHolder licenseHolder) {
|
MainWindowTitleController(AppLifecycleListener appLifecycle, @MainWindow Stage window, FxApplication application, @Named("trayMenuSupported") boolean minimizeToSysTray, UpdateChecker updateChecker, LicenseHolder licenseHolder) {
|
||||||
|
this.appLifecycle = appLifecycle;
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.application = application;
|
this.application = application;
|
||||||
this.minimizeToSysTray = minimizeToSysTray;
|
this.minimizeToSysTray = minimizeToSysTray;
|
||||||
@@ -56,6 +57,10 @@ public class MainWindowTitleController implements FxController {
|
|||||||
window.setX(event.getScreenX() - xOffset);
|
window.setX(event.getScreenX() - xOffset);
|
||||||
window.setY(event.getScreenY() - yOffset);
|
window.setY(event.getScreenY() - yOffset);
|
||||||
});
|
});
|
||||||
|
window.setOnCloseRequest(event -> {
|
||||||
|
close();
|
||||||
|
event.consume();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -63,10 +68,15 @@ public class MainWindowTitleController implements FxController {
|
|||||||
if (minimizeToSysTray) {
|
if (minimizeToSysTray) {
|
||||||
window.close();
|
window.close();
|
||||||
} else {
|
} else {
|
||||||
window.setIconified(true);
|
appLifecycle.quit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void minimize() {
|
||||||
|
window.setIconified(true);
|
||||||
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void showPreferences() {
|
public void showPreferences() {
|
||||||
application.showPreferencesWindow(SelectedPreferencesTab.ANY);
|
application.showPreferencesWindow(SelectedPreferencesTab.ANY);
|
||||||
@@ -91,5 +101,7 @@ public class MainWindowTitleController implements FxController {
|
|||||||
return updateAvailable.get();
|
return updateAvailable.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isMinimizeToSysTray() {
|
||||||
|
return minimizeToSysTray;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,11 +33,11 @@ public class VaultDetailController implements FxController {
|
|||||||
private FontAwesome5Icon getGlyphForVaultState(VaultState state) {
|
private FontAwesome5Icon getGlyphForVaultState(VaultState state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case LOCKED:
|
case LOCKED:
|
||||||
return FontAwesome5Icon.LOCK_ALT;
|
return FontAwesome5Icon.LOCK;
|
||||||
case PROCESSING:
|
case PROCESSING:
|
||||||
return FontAwesome5Icon.SPINNER;
|
return FontAwesome5Icon.SPINNER;
|
||||||
case UNLOCKED:
|
case UNLOCKED:
|
||||||
return FontAwesome5Icon.LOCK_OPEN_ALT;
|
return FontAwesome5Icon.LOCK_OPEN;
|
||||||
default:
|
default:
|
||||||
return FontAwesome5Icon.EXCLAMATION_TRIANGLE;
|
return FontAwesome5Icon.EXCLAMATION_TRIANGLE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,11 +25,11 @@ public class VaultListCellController implements FxController {
|
|||||||
private FontAwesome5Icon getGlyphForVaultState(VaultState state) {
|
private FontAwesome5Icon getGlyphForVaultState(VaultState state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case LOCKED:
|
case LOCKED:
|
||||||
return FontAwesome5Icon.LOCK_ALT;
|
return FontAwesome5Icon.LOCK;
|
||||||
case PROCESSING:
|
case PROCESSING:
|
||||||
return FontAwesome5Icon.SPINNER;
|
return FontAwesome5Icon.SPINNER;
|
||||||
case UNLOCKED:
|
case UNLOCKED:
|
||||||
return FontAwesome5Icon.LOCK_OPEN_ALT;
|
return FontAwesome5Icon.LOCK_OPEN;
|
||||||
default:
|
default:
|
||||||
return FontAwesome5Icon.EXCLAMATION_TRIANGLE;
|
return FontAwesome5Icon.EXCLAMATION_TRIANGLE;
|
||||||
}
|
}
|
||||||
|
|||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
package org.cryptomator.ui.migration;
|
||||||
|
|
||||||
|
import dagger.Lazy;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.binding.StringBinding;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
@MigrationScoped
|
||||||
|
public class MigrationCapabilityErrorController implements FxController {
|
||||||
|
|
||||||
|
private final Stage window;
|
||||||
|
private final ResourceBundle localization;
|
||||||
|
private final Lazy<Scene> startScene;
|
||||||
|
private final StringBinding missingCapabilityDescription;
|
||||||
|
private final ReadOnlyObjectProperty<FileSystemCapabilityChecker.Capability> missingCapability;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MigrationCapabilityErrorController(@MigrationWindow Stage window, @Named("capabilityErrorCause") ObjectProperty<FileSystemCapabilityChecker.Capability> missingCapability, ResourceBundle localization, @FxmlScene(FxmlFile.MIGRATION_START) Lazy<Scene> startScene) {
|
||||||
|
this.window = window;
|
||||||
|
this.missingCapability = missingCapability;
|
||||||
|
this.localization = localization;
|
||||||
|
this.startScene = startScene;
|
||||||
|
this.missingCapabilityDescription = Bindings.createStringBinding(this::getMissingCapabilityDescription, missingCapability);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void back() {
|
||||||
|
window.setScene(startScene.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Getters */
|
||||||
|
|
||||||
|
public StringBinding missingCapabilityDescriptionProperty() {
|
||||||
|
return missingCapabilityDescription;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMissingCapabilityDescription() {
|
||||||
|
FileSystemCapabilityChecker.Capability c = missingCapability.get();
|
||||||
|
if (c != null) {
|
||||||
|
return localization.getString("migration.error.missingFileSystemCapabilities.reason." + c.name());
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package org.cryptomator.ui.migration;
|
||||||
|
|
||||||
|
import dagger.Lazy;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@MigrationScoped
|
||||||
|
public class MigrationGenericErrorController implements FxController {
|
||||||
|
|
||||||
|
private final Stage window;
|
||||||
|
private final Lazy<Scene> startScene;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MigrationGenericErrorController(@MigrationWindow Stage window, @FxmlScene(FxmlFile.MIGRATION_START) Lazy<Scene> startScene) {
|
||||||
|
this.window = window;
|
||||||
|
this.startScene = startScene;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void back() {
|
||||||
|
window.setScene(startScene.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,22 +4,26 @@ import dagger.Binds;
|
|||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import dagger.multibindings.IntoMap;
|
import dagger.multibindings.IntoMap;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.stage.Modality;
|
import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
|
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
|
||||||
import org.cryptomator.ui.common.DefaultSceneFactory;
|
import org.cryptomator.ui.common.DefaultSceneFactory;
|
||||||
import org.cryptomator.ui.common.FXMLLoaderFactory;
|
import org.cryptomator.ui.common.FXMLLoaderFactory;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.FxControllerKey;
|
import org.cryptomator.ui.common.FxControllerKey;
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
|
import org.cryptomator.ui.common.StackTraceController;
|
||||||
import org.cryptomator.ui.mainwindow.MainWindow;
|
import org.cryptomator.ui.mainwindow.MainWindow;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@@ -35,16 +39,30 @@ abstract class MigrationModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@MigrationWindow
|
@MigrationWindow
|
||||||
@MigrationScoped
|
@MigrationScoped
|
||||||
static Stage provideStage(@MainWindow Stage owner, ResourceBundle resourceBundle, @Named("windowIcon") Optional<Image> windowIcon) {
|
static Stage provideStage(@MainWindow Stage owner, ResourceBundle resourceBundle, @Named("windowIcons") List<Image> windowIcons) {
|
||||||
Stage stage = new Stage();
|
Stage stage = new Stage();
|
||||||
stage.setTitle(resourceBundle.getString("migration.title"));
|
stage.setTitle(resourceBundle.getString("migration.title"));
|
||||||
stage.setResizable(false);
|
stage.setResizable(false);
|
||||||
stage.initModality(Modality.WINDOW_MODAL);
|
stage.initModality(Modality.WINDOW_MODAL);
|
||||||
stage.initOwner(owner);
|
stage.initOwner(owner);
|
||||||
windowIcon.ifPresent(stage.getIcons()::add);
|
stage.getIcons().addAll(windowIcons);
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Named("genericErrorCause")
|
||||||
|
@MigrationScoped
|
||||||
|
static ObjectProperty<Throwable> provideGenericErrorCause() {
|
||||||
|
return new SimpleObjectProperty<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Named("capabilityErrorCause")
|
||||||
|
@MigrationScoped
|
||||||
|
static ObjectProperty<FileSystemCapabilityChecker.Capability> provideCapabilityErrorCause() {
|
||||||
|
return new SimpleObjectProperty<>();
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@FxmlScene(FxmlFile.MIGRATION_START)
|
@FxmlScene(FxmlFile.MIGRATION_START)
|
||||||
@MigrationScoped
|
@MigrationScoped
|
||||||
@@ -66,6 +84,21 @@ abstract class MigrationModule {
|
|||||||
return fxmlLoaders.createScene("/fxml/migration_success.fxml");
|
return fxmlLoaders.createScene("/fxml/migration_success.fxml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxmlScene(FxmlFile.MIGRATION_CAPABILITY_ERROR)
|
||||||
|
@MigrationScoped
|
||||||
|
static Scene provideMigrationCapabilityErrorScene(@MigrationWindow FXMLLoaderFactory fxmlLoaders) {
|
||||||
|
return fxmlLoaders.createScene("/fxml/migration_capability_error.fxml");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxmlScene(FxmlFile.MIGRATION_GENERIC_ERROR)
|
||||||
|
@MigrationScoped
|
||||||
|
static Scene provideMigrationGenericErrorScene(@MigrationWindow FXMLLoaderFactory fxmlLoaders) {
|
||||||
|
return fxmlLoaders.createScene("/fxml/migration_generic_error.fxml");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ------------------
|
// ------------------
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@@ -83,4 +116,21 @@ abstract class MigrationModule {
|
|||||||
@FxControllerKey(MigrationSuccessController.class)
|
@FxControllerKey(MigrationSuccessController.class)
|
||||||
abstract FxController bindMigrationSuccessController(MigrationSuccessController controller);
|
abstract FxController bindMigrationSuccessController(MigrationSuccessController controller);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(MigrationCapabilityErrorController.class)
|
||||||
|
abstract FxController bindMigrationCapabilityErrorController(MigrationCapabilityErrorController controller);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(MigrationGenericErrorController.class)
|
||||||
|
abstract FxController bindMigrationGenericErrorController(MigrationGenericErrorController controller);
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(StackTraceController.class)
|
||||||
|
static FxController provideStackTraceController(@Named("genericErrorCause") ObjectProperty<Throwable> errorCause) {
|
||||||
|
return new StackTraceController(errorCause.get());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,28 @@
|
|||||||
package org.cryptomator.ui.migration;
|
package org.cryptomator.ui.migration;
|
||||||
|
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import javafx.animation.KeyFrame;
|
import javafx.application.Platform;
|
||||||
import javafx.animation.KeyValue;
|
|
||||||
import javafx.animation.Timeline;
|
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.binding.ObjectBinding;
|
import javafx.beans.binding.ObjectBinding;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.DoubleProperty;
|
import javafx.beans.property.DoubleProperty;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.ReadOnlyDoubleProperty;
|
import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.property.SimpleDoubleProperty;
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
import javafx.beans.value.WritableValue;
|
|
||||||
import javafx.concurrent.ScheduledService;
|
|
||||||
import javafx.concurrent.Task;
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.ContentDisplay;
|
import javafx.scene.control.ContentDisplay;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.util.Duration;
|
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultState;
|
import org.cryptomator.common.vaults.VaultState;
|
||||||
|
import org.cryptomator.cryptofs.common.FileSystemCapabilityChecker;
|
||||||
import org.cryptomator.cryptofs.migration.Migrators;
|
import org.cryptomator.cryptofs.migration.Migrators;
|
||||||
import org.cryptomator.cryptofs.migration.api.MigrationProgressListener;
|
import org.cryptomator.cryptofs.migration.api.MigrationProgressListener;
|
||||||
import org.cryptomator.cryptofs.migration.api.NoApplicableMigratorException;
|
|
||||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||||
import org.cryptomator.keychain.KeychainAccess;
|
import org.cryptomator.keychain.KeychainAccess;
|
||||||
import org.cryptomator.keychain.KeychainAccessException;
|
import org.cryptomator.keychain.KeychainAccessException;
|
||||||
|
import org.cryptomator.ui.common.Animations;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
@@ -36,44 +32,54 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
@MigrationScoped
|
@MigrationScoped
|
||||||
public class MigrationRunController implements FxController {
|
public class MigrationRunController implements FxController {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(MigrationRunController.class);
|
private static final Logger LOG = LoggerFactory.getLogger(MigrationRunController.class);
|
||||||
private static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; // TODO: deduplicate constant declared in multiple classes
|
private static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; // TODO: deduplicate constant declared in multiple classes
|
||||||
private static final Duration MIGRATION_PROGRESS_UPDATE_INTERVAL = Duration.millis(25);
|
private static final long MIGRATION_PROGRESS_UPDATE_MILLIS = 50;
|
||||||
|
|
||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final Vault vault;
|
private final Vault vault;
|
||||||
private final ExecutorService executor;
|
private final ExecutorService executor;
|
||||||
|
private final ScheduledExecutorService scheduler;
|
||||||
private final Optional<KeychainAccess> keychainAccess;
|
private final Optional<KeychainAccess> keychainAccess;
|
||||||
|
private final ObjectProperty<FileSystemCapabilityChecker.Capability> missingCapability;
|
||||||
|
private final ObjectProperty<Throwable> errorCause;
|
||||||
private final Lazy<Scene> startScene;
|
private final Lazy<Scene> startScene;
|
||||||
private final Lazy<Scene> successScene;
|
private final Lazy<Scene> successScene;
|
||||||
private final ObjectBinding<ContentDisplay> migrateButtonContentDisplay;
|
private final ObjectBinding<ContentDisplay> migrateButtonContentDisplay;
|
||||||
|
private final Lazy<Scene> capabilityErrorScene;
|
||||||
|
private final Lazy<Scene> genericErrorScene;
|
||||||
private final BooleanProperty migrationButtonDisabled;
|
private final BooleanProperty migrationButtonDisabled;
|
||||||
private final DoubleProperty migrationProgress;
|
private final DoubleProperty migrationProgress;
|
||||||
private final ScheduledService<Double> migrationProgressObservationService;
|
|
||||||
private volatile double volatileMigrationProgress = -1.0;
|
private volatile double volatileMigrationProgress = -1.0;
|
||||||
public NiceSecurePasswordField passwordField;
|
public NiceSecurePasswordField passwordField;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public MigrationRunController(@MigrationWindow Stage window, @MigrationWindow Vault vault, ExecutorService executor, Optional<KeychainAccess> keychainAccess, @FxmlScene(FxmlFile.MIGRATION_START) Lazy<Scene> startScene, @FxmlScene(FxmlFile.MIGRATION_SUCCESS) Lazy<Scene> successScene) {
|
public MigrationRunController(@MigrationWindow Stage window, @MigrationWindow Vault vault, ExecutorService executor, ScheduledExecutorService scheduler, Optional<KeychainAccess> keychainAccess, @Named("capabilityErrorCause") ObjectProperty<FileSystemCapabilityChecker.Capability> missingCapability, @Named("genericErrorCause") ObjectProperty<Throwable> errorCause, @FxmlScene(FxmlFile.MIGRATION_START) Lazy<Scene> startScene, @FxmlScene(FxmlFile.MIGRATION_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.MIGRATION_CAPABILITY_ERROR) Lazy<Scene> capabilityErrorScene, @FxmlScene(FxmlFile.MIGRATION_GENERIC_ERROR) Lazy<Scene> genericErrorScene) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
|
this.scheduler = scheduler;
|
||||||
this.keychainAccess = keychainAccess;
|
this.keychainAccess = keychainAccess;
|
||||||
|
this.missingCapability = missingCapability;
|
||||||
|
this.errorCause = errorCause;
|
||||||
this.startScene = startScene;
|
this.startScene = startScene;
|
||||||
this.successScene = successScene;
|
this.successScene = successScene;
|
||||||
this.migrateButtonContentDisplay = Bindings.createObjectBinding(this::getMigrateButtonContentDisplay, vault.stateProperty());
|
this.migrateButtonContentDisplay = Bindings.createObjectBinding(this::getMigrateButtonContentDisplay, vault.stateProperty());
|
||||||
|
this.capabilityErrorScene = capabilityErrorScene;
|
||||||
|
this.genericErrorScene = genericErrorScene;
|
||||||
this.migrationButtonDisabled = new SimpleBooleanProperty();
|
this.migrationButtonDisabled = new SimpleBooleanProperty();
|
||||||
this.migrationProgress = new SimpleDoubleProperty(volatileMigrationProgress);
|
this.migrationProgress = new SimpleDoubleProperty(volatileMigrationProgress);
|
||||||
this.migrationProgressObservationService = new MigrationProgressObservationService();
|
|
||||||
migrationProgressObservationService.setExecutor(executor);
|
|
||||||
migrationProgressObservationService.setPeriod(MIGRATION_PROGRESS_UPDATE_INTERVAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initialize() {
|
public void initialize() {
|
||||||
@@ -93,36 +99,42 @@ public class MigrationRunController implements FxController {
|
|||||||
LOG.info("Migrating vault {}", vault.getPath());
|
LOG.info("Migrating vault {}", vault.getPath());
|
||||||
CharSequence password = passwordField.getCharacters();
|
CharSequence password = passwordField.getCharacters();
|
||||||
vault.setState(VaultState.PROCESSING);
|
vault.setState(VaultState.PROCESSING);
|
||||||
migrationProgressObservationService.start();
|
ScheduledFuture<?> progressSyncTask = scheduler.scheduleAtFixedRate(() -> {
|
||||||
|
Platform.runLater(() -> {
|
||||||
|
migrationProgress.set(volatileMigrationProgress);
|
||||||
|
});
|
||||||
|
}, 0, MIGRATION_PROGRESS_UPDATE_MILLIS, TimeUnit.MILLISECONDS);
|
||||||
Tasks.create(() -> {
|
Tasks.create(() -> {
|
||||||
Migrators migrators = Migrators.get();
|
Migrators migrators = Migrators.get();
|
||||||
migrators.migrate(vault.getPath(), MASTERKEY_FILENAME, password, this::migrationProgressChanged);
|
migrators.migrate(vault.getPath(), MASTERKEY_FILENAME, password, this::migrationProgressChanged);
|
||||||
return migrators.needsMigration(vault.getPath(), MASTERKEY_FILENAME);
|
return migrators.needsMigration(vault.getPath(), MASTERKEY_FILENAME);
|
||||||
}).onSuccess(needsAnotherMigration -> {
|
}).onSuccess(needsAnotherMigration -> {
|
||||||
LOG.info("Migration of '{}' succeeded.", vault.getDisplayableName());
|
|
||||||
if (needsAnotherMigration) {
|
if (needsAnotherMigration) {
|
||||||
|
LOG.info("Migration of '{}' succeeded, but another migration is required.", vault.getDisplayableName());
|
||||||
vault.setState(VaultState.NEEDS_MIGRATION);
|
vault.setState(VaultState.NEEDS_MIGRATION);
|
||||||
} else {
|
} else {
|
||||||
|
LOG.info("Migration of '{}' succeeded.", vault.getDisplayableName());
|
||||||
vault.setState(VaultState.LOCKED);
|
vault.setState(VaultState.LOCKED);
|
||||||
passwordField.swipe();
|
passwordField.swipe();
|
||||||
window.setScene(successScene.get());
|
window.setScene(successScene.get());
|
||||||
}
|
}
|
||||||
}).onError(InvalidPassphraseException.class, e -> {
|
}).onError(InvalidPassphraseException.class, e -> {
|
||||||
shakeWindow();
|
Animations.createShakeWindowAnimation(window).play();
|
||||||
passwordField.selectAll();
|
passwordField.selectAll();
|
||||||
passwordField.requestFocus();
|
passwordField.requestFocus();
|
||||||
vault.setState(VaultState.NEEDS_MIGRATION);
|
vault.setState(VaultState.NEEDS_MIGRATION);
|
||||||
}).onError(NoApplicableMigratorException.class, e -> {
|
}).onError(FileSystemCapabilityChecker.MissingCapabilityException.class, e -> {
|
||||||
LOG.error("Can not migrate vault.", e);
|
LOG.error("Underlying file system not supported.", e);
|
||||||
vault.setState(VaultState.ERROR);
|
vault.setState(VaultState.ERROR);
|
||||||
// TODO show specific error screen
|
missingCapability.set(e.getMissingCapability());
|
||||||
|
window.setScene(capabilityErrorScene.get());
|
||||||
}).onError(Exception.class, e -> { // including RuntimeExceptions
|
}).onError(Exception.class, e -> { // including RuntimeExceptions
|
||||||
LOG.error("Migration failed for technical reasons.", e);
|
LOG.error("Migration failed for technical reasons.", e);
|
||||||
vault.setState(VaultState.ERROR);
|
vault.setState(VaultState.NEEDS_MIGRATION);
|
||||||
// TODO show generic error screen
|
errorCause.set(e);
|
||||||
|
window.setScene(genericErrorScene.get());
|
||||||
}).andFinally(() -> {
|
}).andFinally(() -> {
|
||||||
migrationProgressObservationService.cancel();
|
progressSyncTask.cancel(true);
|
||||||
migrationProgressObservationService.reset();
|
|
||||||
}).runOnce(executor);
|
}).runOnce(executor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,54 +173,6 @@ public class MigrationRunController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets migrationProgress to volatileMigrationProgress at its configured interval
|
|
||||||
private class MigrationProgressObservationService extends ScheduledService<Double> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Task<Double> createTask() {
|
|
||||||
return new Task<>() {
|
|
||||||
@Override
|
|
||||||
protected Double call() {
|
|
||||||
return volatileMigrationProgress;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void succeeded() {
|
|
||||||
assert getValue() != null;
|
|
||||||
migrationProgress.set(getValue());
|
|
||||||
super.succeeded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Animations */
|
|
||||||
|
|
||||||
private void shakeWindow() {
|
|
||||||
WritableValue<Double> writableWindowX = new WritableValue<>() {
|
|
||||||
@Override
|
|
||||||
public Double getValue() {
|
|
||||||
return window.getX();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setValue(Double value) {
|
|
||||||
window.setX(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Timeline timeline = new Timeline( //
|
|
||||||
new KeyFrame(Duration.ZERO, new KeyValue(writableWindowX, window.getX())), //
|
|
||||||
new KeyFrame(new Duration(100), new KeyValue(writableWindowX, window.getX() - 22.0)), //
|
|
||||||
new KeyFrame(new Duration(200), new KeyValue(writableWindowX, window.getX() + 18.0)), //
|
|
||||||
new KeyFrame(new Duration(300), new KeyValue(writableWindowX, window.getX() - 14.0)), //
|
|
||||||
new KeyFrame(new Duration(400), new KeyValue(writableWindowX, window.getX() + 10.0)), //
|
|
||||||
new KeyFrame(new Duration(500), new KeyValue(writableWindowX, window.getX() - 6.0)), //
|
|
||||||
new KeyFrame(new Duration(600), new KeyValue(writableWindowX, window.getX() + 2.0)), //
|
|
||||||
new KeyFrame(new Duration(700), new KeyValue(writableWindowX, window.getX())) //
|
|
||||||
);
|
|
||||||
timeline.play();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Getter/Setter */
|
/* Getter/Setter */
|
||||||
|
|
||||||
public Vault getVault() {
|
public Vault getVault() {
|
||||||
|
|||||||
@@ -18,8 +18,8 @@ import org.cryptomator.ui.common.FxmlScene;
|
|||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@Module(includes = {AutoStartModule.class})
|
@Module(includes = {AutoStartModule.class})
|
||||||
@@ -41,11 +41,11 @@ abstract class PreferencesModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@PreferencesWindow
|
@PreferencesWindow
|
||||||
@PreferencesScoped
|
@PreferencesScoped
|
||||||
static Stage provideStage(ResourceBundle resourceBundle, @Named("windowIcon") Optional<Image> windowIcon) {
|
static Stage provideStage(ResourceBundle resourceBundle, @Named("windowIcons") List<Image> windowIcons) {
|
||||||
Stage stage = new Stage();
|
Stage stage = new Stage();
|
||||||
stage.setTitle(resourceBundle.getString("preferences.title"));
|
stage.setTitle(resourceBundle.getString("preferences.title"));
|
||||||
stage.setResizable(false);
|
stage.setResizable(false);
|
||||||
windowIcon.ifPresent(stage.getIcons()::add);
|
stage.getIcons().addAll(windowIcons);
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ import org.cryptomator.ui.common.FxmlScene;
|
|||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@@ -37,12 +37,12 @@ abstract class QuitModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@QuitWindow
|
@QuitWindow
|
||||||
@QuitScoped
|
@QuitScoped
|
||||||
static Stage provideStage(@Named("windowIcon") Optional<Image> windowIcon) {
|
static Stage provideStage(@Named("windowIcons") List<Image> windowIcons) {
|
||||||
Stage stage = new Stage();
|
Stage stage = new Stage();
|
||||||
stage.setMinWidth(300);
|
stage.setMinWidth(300);
|
||||||
stage.setMinHeight(100);
|
stage.setMinHeight(100);
|
||||||
stage.initModality(Modality.APPLICATION_MODAL);
|
stage.initModality(Modality.APPLICATION_MODAL);
|
||||||
windowIcon.ifPresent(stage.getIcons()::add);
|
stage.getIcons().addAll(windowIcons);
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package org.cryptomator.ui.recoverykey;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class AutoCompleter {
|
||||||
|
|
||||||
|
private final List<String> dictionary;
|
||||||
|
|
||||||
|
public AutoCompleter(Collection<String> dictionary) {
|
||||||
|
this.dictionary = unmodifiableSortedRandomAccessList(dictionary);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T extends Comparable<T>> List<T> unmodifiableSortedRandomAccessList(Collection<T> items) {
|
||||||
|
List<T> result = new ArrayList<>(items);
|
||||||
|
Collections.sort(result);
|
||||||
|
return Collections.unmodifiableList(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> autocomplete(String prefix) {
|
||||||
|
if (Strings.isNullOrEmpty(prefix)) {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
int potentialMatchIdx = findIndexOfLexicographicallyPreceeding(0, dictionary.size(), prefix);
|
||||||
|
if (potentialMatchIdx < dictionary.size()) {
|
||||||
|
String potentialMatch = dictionary.get(potentialMatchIdx);
|
||||||
|
return potentialMatch.startsWith(prefix) ? Optional.of(potentialMatch) : Optional.empty();
|
||||||
|
} else {
|
||||||
|
return Optional.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the index of the first word in {@link #dictionary} that starts with a given prefix.
|
||||||
|
*
|
||||||
|
* This method performs an "unsuccessful" binary search (it doesn't return when encountering an exact match).
|
||||||
|
* Instead it continues searching in the left half (which includes the exact match) until only one element is left.
|
||||||
|
*
|
||||||
|
* If the dictionary doesn't contain a word "left" of the given prefix, this method returns an invalid index, though.
|
||||||
|
*
|
||||||
|
* @param begin Index of first element (inclusive)
|
||||||
|
* @param end Index of last element (exclusive)
|
||||||
|
* @param prefix
|
||||||
|
* @return index between [0, dictLen], i.e. index can exceed the upper bounds of {@link #dictionary}.
|
||||||
|
*/
|
||||||
|
private int findIndexOfLexicographicallyPreceeding(int begin, int end, String prefix) {
|
||||||
|
if (begin >= end) {
|
||||||
|
return begin; // this is usually where a binary search ends "unsuccessful"
|
||||||
|
}
|
||||||
|
|
||||||
|
int mid = (begin + end) / 2;
|
||||||
|
String word = dictionary.get(mid);
|
||||||
|
if (prefix.compareTo(word) <= 0) { // prefix preceeds or matches word
|
||||||
|
// proceed in left half
|
||||||
|
assert mid < end;
|
||||||
|
return findIndexOfLexicographicallyPreceeding(0, mid, prefix);
|
||||||
|
} else {
|
||||||
|
// proceed in right half
|
||||||
|
assert mid >= begin;
|
||||||
|
return findIndexOfLexicographicallyPreceeding(mid + 1, end, prefix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -21,11 +21,21 @@ public interface RecoveryKeyComponent {
|
|||||||
Stage window();
|
Stage window();
|
||||||
|
|
||||||
@FxmlScene(FxmlFile.RECOVERYKEY_CREATE)
|
@FxmlScene(FxmlFile.RECOVERYKEY_CREATE)
|
||||||
Lazy<Scene> scene();
|
Lazy<Scene> creationScene();
|
||||||
|
|
||||||
|
@FxmlScene(FxmlFile.RECOVERYKEY_RECOVER)
|
||||||
|
Lazy<Scene> recoverScene();
|
||||||
|
|
||||||
default void showRecoveryKeyCreationWindow() {
|
default void showRecoveryKeyCreationWindow() {
|
||||||
Stage stage = window();
|
Stage stage = window();
|
||||||
stage.setScene(scene().get());
|
stage.setScene(creationScene().get());
|
||||||
|
stage.sizeToScene();
|
||||||
|
stage.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
default void showRecoveryKeyRecoverWindow() {
|
||||||
|
Stage stage = window();
|
||||||
|
stage.setScene(recoverScene().get());
|
||||||
stage.sizeToScene();
|
stage.sizeToScene();
|
||||||
stage.show();
|
stage.show();
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-41
@@ -1,27 +1,21 @@
|
|||||||
package org.cryptomator.ui.recoverykey;
|
package org.cryptomator.ui.recoverykey;
|
||||||
|
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import javafx.animation.KeyFrame;
|
|
||||||
import javafx.animation.KeyValue;
|
|
||||||
import javafx.animation.Timeline;
|
|
||||||
import javafx.beans.property.ReadOnlyStringProperty;
|
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.beans.value.WritableValue;
|
import javafx.concurrent.Task;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.util.Duration;
|
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||||
|
import org.cryptomator.ui.common.Animations;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
import org.cryptomator.ui.common.Tasks;
|
|
||||||
import org.cryptomator.ui.controls.NiceSecurePasswordField;
|
import org.cryptomator.ui.controls.NiceSecurePasswordField;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
@@ -48,19 +42,26 @@ public class RecoveryKeyCreationController implements FxController {
|
|||||||
this.recoveryKeyFactory = recoveryKeyFactory;
|
this.recoveryKeyFactory = recoveryKeyFactory;
|
||||||
this.recoveryKeyProperty = recoveryKey;
|
this.recoveryKeyProperty = recoveryKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void createRecoveryKey() {
|
public void createRecoveryKey() {
|
||||||
Tasks.create(() -> {
|
Task<String> task = new RecoveryKeyCreationTask();
|
||||||
return recoveryKeyFactory.createRecoveryKey(vault.getPath(), passwordField.getCharacters());
|
task.setOnScheduled(event -> {
|
||||||
}).onSuccess(result -> {
|
LOG.debug("Creating recovery key for {}.", vault.getDisplayablePath());
|
||||||
recoveryKeyProperty.set(result);
|
});
|
||||||
|
task.setOnSucceeded(event -> {
|
||||||
|
String recoveryKey = task.getValue();
|
||||||
|
recoveryKeyProperty.set(recoveryKey);
|
||||||
window.setScene(successScene.get());
|
window.setScene(successScene.get());
|
||||||
}).onError(IOException.class, e -> {
|
});
|
||||||
LOG.error("Creation of recovery key failed.", e);
|
task.setOnFailed(event -> {
|
||||||
}).onError(InvalidPassphraseException.class, e -> {
|
if (task.getException() instanceof InvalidPassphraseException) {
|
||||||
shakeWindow();
|
Animations.createShakeWindowAnimation(window).play();
|
||||||
}).runOnce(executor);
|
} else {
|
||||||
|
LOG.error("Creation of recovery key failed.", task.getException());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
executor.submit(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -68,31 +69,13 @@ public class RecoveryKeyCreationController implements FxController {
|
|||||||
window.close();
|
window.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animations */
|
private class RecoveryKeyCreationTask extends Task<String> {
|
||||||
|
|
||||||
private void shakeWindow() {
|
@Override
|
||||||
WritableValue<Double> writableWindowX = new WritableValue<>() {
|
protected String call() throws IOException {
|
||||||
@Override
|
return recoveryKeyFactory.createRecoveryKey(vault.getPath(), passwordField.getCharacters());
|
||||||
public Double getValue() {
|
}
|
||||||
return window.getX();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setValue(Double value) {
|
|
||||||
window.setX(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Timeline timeline = new Timeline( //
|
|
||||||
new KeyFrame(Duration.ZERO, new KeyValue(writableWindowX, window.getX())), //
|
|
||||||
new KeyFrame(new Duration(100), new KeyValue(writableWindowX, window.getX() - 22.0)), //
|
|
||||||
new KeyFrame(new Duration(200), new KeyValue(writableWindowX, window.getX() + 18.0)), //
|
|
||||||
new KeyFrame(new Duration(300), new KeyValue(writableWindowX, window.getX() - 14.0)), //
|
|
||||||
new KeyFrame(new Duration(400), new KeyValue(writableWindowX, window.getX() + 10.0)), //
|
|
||||||
new KeyFrame(new Duration(500), new KeyValue(writableWindowX, window.getX() - 6.0)), //
|
|
||||||
new KeyFrame(new Duration(600), new KeyValue(writableWindowX, window.getX() + 2.0)), //
|
|
||||||
new KeyFrame(new Duration(700), new KeyValue(writableWindowX, window.getX())) //
|
|
||||||
);
|
|
||||||
timeline.play();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Getter/Setter */
|
/* Getter/Setter */
|
||||||
|
|||||||
+10
-2
@@ -4,6 +4,7 @@ import javafx.fxml.FXML;
|
|||||||
import javafx.print.PageLayout;
|
import javafx.print.PageLayout;
|
||||||
import javafx.print.Printer;
|
import javafx.print.Printer;
|
||||||
import javafx.print.PrinterJob;
|
import javafx.print.PrinterJob;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
import javafx.scene.input.Clipboard;
|
import javafx.scene.input.Clipboard;
|
||||||
import javafx.scene.input.ClipboardContent;
|
import javafx.scene.input.ClipboardContent;
|
||||||
import javafx.scene.text.Font;
|
import javafx.scene.text.Font;
|
||||||
@@ -16,6 +17,8 @@ import org.cryptomator.ui.common.FxController;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
public class RecoveryKeyDisplayController implements FxController {
|
public class RecoveryKeyDisplayController implements FxController {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(RecoveryKeyDisplayController.class);
|
private static final Logger LOG = LoggerFactory.getLogger(RecoveryKeyDisplayController.class);
|
||||||
@@ -23,11 +26,14 @@ public class RecoveryKeyDisplayController implements FxController {
|
|||||||
private final Stage window;
|
private final Stage window;
|
||||||
private final String vaultName;
|
private final String vaultName;
|
||||||
private final String recoveryKey;
|
private final String recoveryKey;
|
||||||
|
private final ResourceBundle localization;
|
||||||
public RecoveryKeyDisplayController(Stage window, String vaultName, String recoveryKey) {
|
public Button copyButton;
|
||||||
|
|
||||||
|
public RecoveryKeyDisplayController(Stage window, String vaultName, String recoveryKey, ResourceBundle localization) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.vaultName = vaultName;
|
this.vaultName = vaultName;
|
||||||
this.recoveryKey = recoveryKey;
|
this.recoveryKey = recoveryKey;
|
||||||
|
this.localization = localization;
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
@@ -68,6 +74,8 @@ public class RecoveryKeyDisplayController implements FxController {
|
|||||||
clipboardContent.putString(recoveryKey);
|
clipboardContent.putString(recoveryKey);
|
||||||
Clipboard.getSystemClipboard().setContent(clipboardContent);
|
Clipboard.getSystemClipboard().setContent(clipboardContent);
|
||||||
LOG.info("Recovery key copied to clipboard.");
|
LOG.info("Recovery key copied to clipboard.");
|
||||||
|
|
||||||
|
copyButton.setText(localization.getString("generic.button.copied"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
|
|||||||
@@ -10,11 +10,13 @@ import javax.inject.Singleton;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class RecoveryKeyFactory {
|
public class RecoveryKeyFactory {
|
||||||
|
|
||||||
private static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; // TODO: deduplicate constant declared in multiple classes
|
private static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; // TODO: deduplicate constant declared in multiple classes
|
||||||
|
private static final byte[] PEPPER = new byte[0];
|
||||||
|
|
||||||
private final WordEncoder wordEncoder;
|
private final WordEncoder wordEncoder;
|
||||||
|
|
||||||
@@ -22,6 +24,10 @@ public class RecoveryKeyFactory {
|
|||||||
public RecoveryKeyFactory(WordEncoder wordEncoder) {
|
public RecoveryKeyFactory(WordEncoder wordEncoder) {
|
||||||
this.wordEncoder = wordEncoder;
|
this.wordEncoder = wordEncoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection<String> getDictionary() {
|
||||||
|
return wordEncoder.getWords();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param vaultPath Path to the storage location of a vault
|
* @param vaultPath Path to the storage location of a vault
|
||||||
@@ -32,7 +38,7 @@ public class RecoveryKeyFactory {
|
|||||||
* @apiNote This is a long-running operation and should be invoked in a background thread
|
* @apiNote This is a long-running operation and should be invoked in a background thread
|
||||||
*/
|
*/
|
||||||
public String createRecoveryKey(Path vaultPath, CharSequence password) throws IOException, InvalidPassphraseException {
|
public String createRecoveryKey(Path vaultPath, CharSequence password) throws IOException, InvalidPassphraseException {
|
||||||
byte[] rawKey = CryptoFileSystemProvider.exportRawKey(vaultPath, MASTERKEY_FILENAME, new byte[0], password);
|
byte[] rawKey = CryptoFileSystemProvider.exportRawKey(vaultPath, MASTERKEY_FILENAME, PEPPER, password);
|
||||||
try {
|
try {
|
||||||
return createRecoveryKey(rawKey);
|
return createRecoveryKey(rawKey);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -53,26 +59,53 @@ public class RecoveryKeyFactory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a completely new masterkey using a recovery key.
|
||||||
|
* @param vaultPath Path to the storage location of a vault
|
||||||
|
* @param recoveryKey A recovery key for this vault
|
||||||
|
* @param newPassword The new password used to encrypt the keys
|
||||||
|
* @throws IOException If the masterkey file could not be written
|
||||||
|
* @throws IllegalArgumentException If the recoveryKey is invalid
|
||||||
|
* @apiNote This is a long-running operation and should be invoked in a background thread
|
||||||
|
*/
|
||||||
|
public void resetPasswordWithRecoveryKey(Path vaultPath, String recoveryKey, CharSequence newPassword) throws IOException, IllegalArgumentException {
|
||||||
|
final byte[] rawKey = decodeRecoveryKey(recoveryKey);
|
||||||
|
try {
|
||||||
|
CryptoFileSystemProvider.restoreRawKey(vaultPath, MASTERKEY_FILENAME, rawKey, PEPPER, newPassword);
|
||||||
|
} finally {
|
||||||
|
Arrays.fill(rawKey, (byte) 0x00);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether a String is a syntactically correct recovery key with a valid checksum
|
* Checks whether a String is a syntactically correct recovery key with a valid checksum
|
||||||
* @param recoveryKey A word sequence which might be a recovery key
|
* @param recoveryKey A word sequence which might be a recovery key
|
||||||
* @return <code>true</code> if this seems to be a legitimate recovery key
|
* @return <code>true</code> if this seems to be a legitimate recovery key
|
||||||
*/
|
*/
|
||||||
public boolean validateRecoveryKey(String recoveryKey) {
|
public boolean validateRecoveryKey(String recoveryKey) {
|
||||||
final byte[] paddedKey;
|
|
||||||
try {
|
try {
|
||||||
paddedKey = wordEncoder.decode(recoveryKey);
|
byte[] key = decodeRecoveryKey(recoveryKey);
|
||||||
|
Arrays.fill(key, (byte) 0x00);
|
||||||
|
return true;
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (paddedKey.length != 66) {
|
}
|
||||||
return false;
|
|
||||||
|
private byte[] decodeRecoveryKey(String recoveryKey) throws IllegalArgumentException {
|
||||||
|
byte[] paddedKey = new byte[0];
|
||||||
|
try {
|
||||||
|
paddedKey = wordEncoder.decode(recoveryKey);
|
||||||
|
Preconditions.checkArgument(paddedKey.length == 66, "Recovery key doesn't consist of 66 bytes.");
|
||||||
|
byte[] rawKey = Arrays.copyOf(paddedKey, 64);
|
||||||
|
byte[] expectedCrc16 = Arrays.copyOfRange(paddedKey, 64, 66);
|
||||||
|
byte[] actualCrc32 = Hashing.crc32().hashBytes(rawKey).asBytes();
|
||||||
|
byte[] actualCrc16 = Arrays.copyOf(actualCrc32, 2);
|
||||||
|
Preconditions.checkArgument(Arrays.equals(expectedCrc16, actualCrc16), "Recovery key has invalid CRC.");
|
||||||
|
return rawKey;
|
||||||
|
} finally {
|
||||||
|
Arrays.fill(paddedKey, (byte) 0x00);
|
||||||
}
|
}
|
||||||
byte[] rawKey = Arrays.copyOf(paddedKey, 64);
|
|
||||||
byte[] expectedCrc16 = Arrays.copyOfRange(paddedKey, 64, 66);
|
|
||||||
byte[] actualCrc32 = Hashing.crc32().hashBytes(rawKey).asBytes();
|
|
||||||
byte[] actualCrc16 = Arrays.copyOf(actualCrc32, 2);
|
|
||||||
return Arrays.equals(expectedCrc16, actualCrc16);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import dagger.Binds;
|
|||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
import dagger.multibindings.IntoMap;
|
import dagger.multibindings.IntoMap;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
@@ -17,11 +19,13 @@ import org.cryptomator.ui.common.FxController;
|
|||||||
import org.cryptomator.ui.common.FxControllerKey;
|
import org.cryptomator.ui.common.FxControllerKey;
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
|
import org.cryptomator.ui.common.NewPasswordController;
|
||||||
|
import org.cryptomator.ui.common.PasswordStrengthUtil;
|
||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@@ -37,13 +41,13 @@ abstract class RecoveryKeyModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@RecoveryKeyWindow
|
@RecoveryKeyWindow
|
||||||
@RecoveryKeyScoped
|
@RecoveryKeyScoped
|
||||||
static Stage provideStage(ResourceBundle resourceBundle, @Named("windowIcon") Optional<Image> windowIcon, @Named("keyRecoveryOwner") Stage owner) {
|
static Stage provideStage(ResourceBundle resourceBundle, @Named("windowIcons") List<Image> windowIcons, @Named("keyRecoveryOwner") Stage owner) {
|
||||||
Stage stage = new Stage();
|
Stage stage = new Stage();
|
||||||
stage.setTitle(resourceBundle.getString("recoveryKey.title"));
|
stage.setTitle(resourceBundle.getString("recoveryKey.title"));
|
||||||
stage.setResizable(false);
|
stage.setResizable(false);
|
||||||
stage.initModality(Modality.WINDOW_MODAL);
|
stage.initModality(Modality.WINDOW_MODAL);
|
||||||
stage.initOwner(owner);
|
stage.initOwner(owner);
|
||||||
windowIcon.ifPresent(stage.getIcons()::add);
|
stage.getIcons().addAll(windowIcons);
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +57,15 @@ abstract class RecoveryKeyModule {
|
|||||||
static StringProperty provideRecoveryKeyProperty() {
|
static StringProperty provideRecoveryKeyProperty() {
|
||||||
return new SimpleStringProperty();
|
return new SimpleStringProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@RecoveryKeyScoped
|
||||||
|
@Named("newPassword")
|
||||||
|
static ObjectProperty<CharSequence> provideNewPasswordProperty() {
|
||||||
|
return new SimpleObjectProperty<>("");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ------------------
|
// ------------------
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@@ -70,6 +82,20 @@ abstract class RecoveryKeyModule {
|
|||||||
return fxmlLoaders.createScene("/fxml/recoverykey_success.fxml");
|
return fxmlLoaders.createScene("/fxml/recoverykey_success.fxml");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxmlScene(FxmlFile.RECOVERYKEY_RECOVER)
|
||||||
|
@RecoveryKeyScoped
|
||||||
|
static Scene provideRecoveryKeyRecoverScene(@RecoveryKeyWindow FXMLLoaderFactory fxmlLoaders) {
|
||||||
|
return fxmlLoaders.createScene("/fxml/recoverykey_recover.fxml");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD)
|
||||||
|
@RecoveryKeyScoped
|
||||||
|
static Scene provideRecoveryKeyResetPasswordScene(@RecoveryKeyWindow FXMLLoaderFactory fxmlLoaders) {
|
||||||
|
return fxmlLoaders.createScene("/fxml/recoverykey_reset_password.fxml");
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------
|
// ------------------
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@@ -80,13 +106,30 @@ abstract class RecoveryKeyModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FxControllerKey(RecoveryKeyDisplayController.class)
|
@FxControllerKey(RecoveryKeyDisplayController.class)
|
||||||
static FxController provideRecoveryKeyDisplayController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, @RecoveryKeyWindow StringProperty recoveryKey) {
|
static FxController provideRecoveryKeyDisplayController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, @RecoveryKeyWindow StringProperty recoveryKey, ResourceBundle localization) {
|
||||||
return new RecoveryKeyDisplayController(window, vault.getDisplayableName(), recoveryKey.get());
|
return new RecoveryKeyDisplayController(window, vault.getDisplayableName(), recoveryKey.get(), localization);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(RecoveryKeyRecoverController.class)
|
||||||
|
abstract FxController provideRecoveryKeyRecoverController(RecoveryKeyRecoverController controller);
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FxControllerKey(RecoveryKeySuccessController.class)
|
@FxControllerKey(RecoveryKeySuccessController.class)
|
||||||
abstract FxController bindRecoveryKeySuccessController(RecoveryKeySuccessController controller);
|
abstract FxController bindRecoveryKeySuccessController(RecoveryKeySuccessController controller);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(RecoveryKeyResetPasswordController.class)
|
||||||
|
abstract FxController bindRecoveryKeyResetPasswordController(RecoveryKeyResetPasswordController controller);
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(NewPasswordController.class)
|
||||||
|
static FxController provideNewPasswordController(ResourceBundle resourceBundle, PasswordStrengthUtil strengthRater, @Named("newPassword") ObjectProperty<CharSequence> password) {
|
||||||
|
return new NewPasswordController(resourceBundle, strengthRater, password);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+116
@@ -0,0 +1,116 @@
|
|||||||
|
package org.cryptomator.ui.recoverykey;
|
||||||
|
|
||||||
|
import com.google.common.base.CharMatcher;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import dagger.Lazy;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.binding.BooleanBinding;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.scene.control.TextArea;
|
||||||
|
import javafx.scene.control.TextFormatter;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.input.KeyEvent;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@RecoveryKeyScoped
|
||||||
|
public class RecoveryKeyRecoverController implements FxController {
|
||||||
|
|
||||||
|
private final static CharMatcher ALLOWED_CHARS = CharMatcher.inRange('a', 'z').or(CharMatcher.is(' '));
|
||||||
|
|
||||||
|
private final Stage window;
|
||||||
|
private final Vault vault;
|
||||||
|
private final StringProperty recoveryKey;
|
||||||
|
private final RecoveryKeyFactory recoveryKeyFactory;
|
||||||
|
private final BooleanBinding validRecoveryKey;
|
||||||
|
private final Lazy<Scene> resetPasswordScene;
|
||||||
|
private final AutoCompleter autoCompleter;
|
||||||
|
|
||||||
|
public TextArea textarea;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public RecoveryKeyRecoverController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, @RecoveryKeyWindow StringProperty recoveryKey, RecoveryKeyFactory recoveryKeyFactory, @FxmlScene(FxmlFile.RECOVERYKEY_RESET_PASSWORD) Lazy<Scene> resetPasswordScene) {
|
||||||
|
this.window = window;
|
||||||
|
this.vault = vault;
|
||||||
|
this.recoveryKey = recoveryKey;
|
||||||
|
this.recoveryKeyFactory = recoveryKeyFactory;
|
||||||
|
this.validRecoveryKey = Bindings.createBooleanBinding(this::isValidRecoveryKey, recoveryKey);
|
||||||
|
this.resetPasswordScene = resetPasswordScene;
|
||||||
|
this.autoCompleter = new AutoCompleter(recoveryKeyFactory.getDictionary());
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void initialize() {
|
||||||
|
recoveryKey.bind(textarea.textProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextFormatter.Change filterTextChange(TextFormatter.Change change) {
|
||||||
|
if (Strings.isNullOrEmpty(change.getText())) {
|
||||||
|
// pass-through caret/selection changes that don't affect the text
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
if (!ALLOWED_CHARS.matchesAllOf(change.getText())) {
|
||||||
|
return null; // reject change
|
||||||
|
}
|
||||||
|
|
||||||
|
String text = change.getControlNewText();
|
||||||
|
int caretPos = change.getCaretPosition();
|
||||||
|
if (caretPos == text.length() || text.charAt(caretPos) == ' ') { // are we at the end of a word?
|
||||||
|
int beginOfWord = Math.max(text.substring(0, caretPos).lastIndexOf(' ') + 1, 0);
|
||||||
|
String currentWord = text.substring(beginOfWord, caretPos);
|
||||||
|
Optional<String> suggestion = autoCompleter.autocomplete(currentWord);
|
||||||
|
if (suggestion.isPresent()) {
|
||||||
|
String completion = suggestion.get().substring(currentWord.length());
|
||||||
|
change.setText(change.getText() + completion);
|
||||||
|
change.setAnchor(caretPos + completion.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void onKeyPressed(KeyEvent keyEvent) {
|
||||||
|
if (keyEvent.getCode() == KeyCode.TAB && textarea.getAnchor() > textarea.getCaretPosition()) {
|
||||||
|
// apply autocompletion:
|
||||||
|
int pos = textarea.getAnchor();
|
||||||
|
textarea.insertText(pos, " ");
|
||||||
|
textarea.positionCaret(pos + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void close() {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void recover() {
|
||||||
|
window.setScene(resetPasswordScene.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Getter/Setter */
|
||||||
|
|
||||||
|
public Vault getVault() {
|
||||||
|
return vault;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanBinding validRecoveryKeyProperty() {
|
||||||
|
return validRecoveryKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValidRecoveryKey() {
|
||||||
|
return recoveryKeyFactory.validateRecoveryKey(recoveryKey.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextFormatter getRecoveryKeyTextFormatter() {
|
||||||
|
return new TextFormatter<>(this::filterTextChange);
|
||||||
|
}
|
||||||
|
}
|
||||||
+94
@@ -0,0 +1,94 @@
|
|||||||
|
package org.cryptomator.ui.recoverykey;
|
||||||
|
|
||||||
|
import dagger.Lazy;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.binding.BooleanBinding;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||||
|
import org.cryptomator.ui.common.Animations;
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
|
@RecoveryKeyScoped
|
||||||
|
public class RecoveryKeyResetPasswordController implements FxController {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(RecoveryKeyResetPasswordController.class);
|
||||||
|
|
||||||
|
private final Stage window;
|
||||||
|
private final Vault vault;
|
||||||
|
private final RecoveryKeyFactory recoveryKeyFactory;
|
||||||
|
private final ExecutorService executor;
|
||||||
|
private final StringProperty recoveryKey;
|
||||||
|
private final ObjectProperty<CharSequence> newPassword;
|
||||||
|
private final Lazy<Scene> recoverScene;
|
||||||
|
private final BooleanBinding invalidNewPassword;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public RecoveryKeyResetPasswordController(@RecoveryKeyWindow Stage window, @RecoveryKeyWindow Vault vault, RecoveryKeyFactory recoveryKeyFactory, ExecutorService executor, @RecoveryKeyWindow StringProperty recoveryKey, @Named("newPassword")ObjectProperty<CharSequence> newPassword, @FxmlScene(FxmlFile.RECOVERYKEY_RECOVER) Lazy<Scene> recoverScene) {
|
||||||
|
this.window = window;
|
||||||
|
this.vault = vault;
|
||||||
|
this.recoveryKeyFactory = recoveryKeyFactory;
|
||||||
|
this.executor = executor;
|
||||||
|
this.recoveryKey = recoveryKey;
|
||||||
|
this.newPassword = newPassword;
|
||||||
|
this.recoverScene = recoverScene;
|
||||||
|
this.invalidNewPassword = Bindings.createBooleanBinding(this::isInvalidNewPassword, newPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void back() {
|
||||||
|
window.setScene(recoverScene.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void done() {
|
||||||
|
Task<Void> task = new ResetPasswordTask();
|
||||||
|
task.setOnScheduled(event -> {
|
||||||
|
LOG.debug("Using recovery key to reset password for {}.", vault.getDisplayablePath());
|
||||||
|
});
|
||||||
|
task.setOnSucceeded(event -> {
|
||||||
|
LOG.info("Used recovery key to reset password for {}.", vault.getDisplayablePath());
|
||||||
|
// TODO show success screen
|
||||||
|
window.close();
|
||||||
|
});
|
||||||
|
task.setOnFailed(event -> {
|
||||||
|
// TODO show generic error screen
|
||||||
|
LOG.error("Creation of recovery key failed.", task.getException());
|
||||||
|
});
|
||||||
|
executor.submit(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ResetPasswordTask extends Task<Void> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void call() throws IOException, IllegalArgumentException {
|
||||||
|
recoveryKeyFactory.resetPasswordWithRecoveryKey(vault.getPath(), recoveryKey.get(), newPassword.get());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Getter/Setter */
|
||||||
|
|
||||||
|
public BooleanBinding invalidNewPasswordProperty() {
|
||||||
|
return invalidNewPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInvalidNewPassword() {
|
||||||
|
return newPassword.get() == null || newPassword.get().length() == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package org.cryptomator.ui.recoverykey;
|
|||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.base.Splitter;
|
import com.google.common.base.Splitter;
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
@@ -11,6 +12,7 @@ import java.io.InputStream;
|
|||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@@ -31,6 +33,10 @@ class WordEncoder {
|
|||||||
this(DEFAULT_WORD_FILE);
|
this(DEFAULT_WORD_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<String> getWords() {
|
||||||
|
return words;
|
||||||
|
}
|
||||||
|
|
||||||
public WordEncoder(String wordFile) {
|
public WordEncoder(String wordFile) {
|
||||||
try (InputStream in = getClass().getResourceAsStream(wordFile); //
|
try (InputStream in = getClass().getResourceAsStream(wordFile); //
|
||||||
Reader reader = new InputStreamReader(in, StandardCharsets.US_ASCII.newDecoder()); //
|
Reader reader = new InputStreamReader(in, StandardCharsets.US_ASCII.newDecoder()); //
|
||||||
@@ -78,7 +84,7 @@ class WordEncoder {
|
|||||||
* @throws IllegalArgumentException If the encoded string doesn't consist of a multiple of two words or one of the words is unknown to this encoder.
|
* @throws IllegalArgumentException If the encoded string doesn't consist of a multiple of two words or one of the words is unknown to this encoder.
|
||||||
*/
|
*/
|
||||||
public byte[] decode(String encoded) {
|
public byte[] decode(String encoded) {
|
||||||
List<String> splitted = Splitter.on(DELIMITER).omitEmptyStrings().splitToList(encoded);
|
List<String> splitted = Splitter.on(DELIMITER).omitEmptyStrings().splitToList(Strings.nullToEmpty(encoded));
|
||||||
Preconditions.checkArgument(splitted.size() % 2 == 0, "%s needs to be a multiple of two words", encoded);
|
Preconditions.checkArgument(splitted.size() % 2 == 0, "%s needs to be a multiple of two words", encoded);
|
||||||
byte[] result = new byte[splitted.size() / 2 * 3];
|
byte[] result = new byte[splitted.size() / 2 * 3];
|
||||||
for (int i = 0; i < splitted.size(); i+=2) {
|
for (int i = 0; i < splitted.size(); i+=2) {
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import org.cryptomator.ui.mainwindow.MainWindow;
|
|||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@@ -38,13 +38,13 @@ abstract class RemoveVaultModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@RemoveVaultWindow
|
@RemoveVaultWindow
|
||||||
@RemoveVaultScoped
|
@RemoveVaultScoped
|
||||||
static Stage provideStage(@MainWindow Stage owner, ResourceBundle resourceBundle, @Named("windowIcon") Optional<Image> windowIcon) {
|
static Stage provideStage(@MainWindow Stage owner, ResourceBundle resourceBundle, @Named("windowIcons") List<Image> windowIcons) {
|
||||||
Stage stage = new Stage();
|
Stage stage = new Stage();
|
||||||
stage.setTitle(resourceBundle.getString("removeVault.title"));
|
stage.setTitle(resourceBundle.getString("removeVault.title"));
|
||||||
stage.setResizable(false);
|
stage.setResizable(false);
|
||||||
stage.initModality(Modality.WINDOW_MODAL);
|
stage.initModality(Modality.WINDOW_MODAL);
|
||||||
stage.initOwner(owner);
|
stage.initOwner(owner);
|
||||||
windowIcon.ifPresent(stage.getIcons()::add);
|
stage.getIcons().addAll(windowIcons);
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,57 +3,37 @@ package org.cryptomator.ui.traymenu;
|
|||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import org.cryptomator.common.ShutdownHook;
|
|
||||||
import org.cryptomator.common.settings.Settings;
|
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultState;
|
import org.cryptomator.ui.launcher.AppLifecycleListener;
|
||||||
import org.cryptomator.common.vaults.Volume;
|
|
||||||
import org.cryptomator.ui.fxapp.FxApplication;
|
|
||||||
import org.cryptomator.ui.launcher.FxApplicationStarter;
|
import org.cryptomator.ui.launcher.FxApplicationStarter;
|
||||||
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
import org.cryptomator.ui.preferences.SelectedPreferencesTab;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Named;
|
|
||||||
import java.awt.Desktop;
|
|
||||||
import java.awt.Menu;
|
import java.awt.Menu;
|
||||||
import java.awt.MenuItem;
|
import java.awt.MenuItem;
|
||||||
import java.awt.PopupMenu;
|
import java.awt.PopupMenu;
|
||||||
import java.awt.desktop.QuitResponse;
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.util.EnumSet;
|
|
||||||
import java.util.EventObject;
|
import java.util.EventObject;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
@TrayMenuScoped
|
@TrayMenuScoped
|
||||||
class TrayMenuController {
|
class TrayMenuController {
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(TrayMenuController.class);
|
|
||||||
public static final Set<VaultState> STATES_ALLOWING_TERMINATION = EnumSet.of(VaultState.LOCKED, VaultState.NEEDS_MIGRATION, VaultState.MISSING, VaultState.ERROR);
|
|
||||||
|
|
||||||
private final ResourceBundle resourceBundle;
|
private final ResourceBundle resourceBundle;
|
||||||
|
private final AppLifecycleListener appLifecycle;
|
||||||
private final FxApplicationStarter fxApplicationStarter;
|
private final FxApplicationStarter fxApplicationStarter;
|
||||||
private final CountDownLatch shutdownLatch;
|
|
||||||
private final ShutdownHook shutdownHook;
|
|
||||||
private final ObservableList<Vault> vaults;
|
private final ObservableList<Vault> vaults;
|
||||||
private final PopupMenu menu;
|
private final PopupMenu menu;
|
||||||
private final AtomicBoolean allowSuddenTermination;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
TrayMenuController(ResourceBundle resourceBundle, FxApplicationStarter fxApplicationStarter, @Named("shutdownLatch") CountDownLatch shutdownLatch, ShutdownHook shutdownHook, ObservableList<Vault> vaults) {
|
TrayMenuController(ResourceBundle resourceBundle, AppLifecycleListener appLifecycle, FxApplicationStarter fxApplicationStarter, ObservableList<Vault> vaults) {
|
||||||
this.resourceBundle = resourceBundle;
|
this.resourceBundle = resourceBundle;
|
||||||
|
this.appLifecycle = appLifecycle;
|
||||||
this.fxApplicationStarter = fxApplicationStarter;
|
this.fxApplicationStarter = fxApplicationStarter;
|
||||||
this.shutdownLatch = shutdownLatch;
|
|
||||||
this.shutdownHook = shutdownHook;
|
|
||||||
this.vaults = vaults;
|
this.vaults = vaults;
|
||||||
this.menu = new PopupMenu();
|
this.menu = new PopupMenu();
|
||||||
this.allowSuddenTermination = new AtomicBoolean(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public PopupMenu getMenu() {
|
public PopupMenu getMenu() {
|
||||||
@@ -62,40 +42,12 @@ class TrayMenuController {
|
|||||||
|
|
||||||
public void initTrayMenu() {
|
public void initTrayMenu() {
|
||||||
vaults.addListener(this::vaultListChanged);
|
vaults.addListener(this::vaultListChanged);
|
||||||
|
|
||||||
rebuildMenu();
|
rebuildMenu();
|
||||||
|
|
||||||
// register preferences shortcut
|
|
||||||
if (Desktop.getDesktop().isSupported(Desktop.Action.APP_PREFERENCES)) {
|
|
||||||
Desktop.getDesktop().setPreferencesHandler(this::showPreferencesWindow);
|
|
||||||
}
|
|
||||||
|
|
||||||
// register quit handler
|
|
||||||
if (Desktop.getDesktop().isSupported(Desktop.Action.APP_QUIT_HANDLER)) {
|
|
||||||
Desktop.getDesktop().setQuitHandler(this::handleQuitRequest);
|
|
||||||
}
|
|
||||||
shutdownHook.runOnShutdown(this::forceUnmountRemainingVaults);
|
|
||||||
|
|
||||||
// allow sudden termination
|
|
||||||
if (Desktop.getDesktop().isSupported(Desktop.Action.APP_SUDDEN_TERMINATION)) {
|
|
||||||
Desktop.getDesktop().enableSuddenTermination();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void vaultListChanged(@SuppressWarnings("unused") Observable observable) {
|
private void vaultListChanged(@SuppressWarnings("unused") Observable observable) {
|
||||||
assert Platform.isFxApplicationThread();
|
assert Platform.isFxApplicationThread();
|
||||||
rebuildMenu();
|
rebuildMenu();
|
||||||
boolean allVaultsAllowTermination = vaults.stream().map(Vault::getState).allMatch(STATES_ALLOWING_TERMINATION::contains);
|
|
||||||
boolean suddenTerminationChanged = allowSuddenTermination.compareAndSet(!allVaultsAllowTermination, allVaultsAllowTermination);
|
|
||||||
if (suddenTerminationChanged && Desktop.getDesktop().isSupported(Desktop.Action.APP_SUDDEN_TERMINATION)) {
|
|
||||||
if (allVaultsAllowTermination) {
|
|
||||||
Desktop.getDesktop().enableSuddenTermination();
|
|
||||||
LOG.debug("sudden termination enabled");
|
|
||||||
} else {
|
|
||||||
Desktop.getDesktop().disableSuddenTermination();
|
|
||||||
LOG.debug("sudden termination disabled");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void rebuildMenu() {
|
private void rebuildMenu() {
|
||||||
@@ -174,37 +126,8 @@ class TrayMenuController {
|
|||||||
fxApplicationStarter.get(true).thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY));
|
fxApplicationStarter.get(true).thenAccept(app -> app.showPreferencesWindow(SelectedPreferencesTab.ANY));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleQuitRequest(EventObject e, QuitResponse response) {
|
|
||||||
if (allowSuddenTermination.get()) {
|
|
||||||
response.performQuit(); // really?
|
|
||||||
} else {
|
|
||||||
fxApplicationStarter.get(true).thenAccept(app -> app.showQuitWindow(response));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void quitApplication(EventObject actionEvent) {
|
private void quitApplication(EventObject actionEvent) {
|
||||||
handleQuitRequest(actionEvent, new QuitResponse() {
|
appLifecycle.quit();
|
||||||
@Override
|
|
||||||
public void performQuit() {
|
|
||||||
shutdownLatch.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void cancelQuit() {
|
|
||||||
// no-op
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void forceUnmountRemainingVaults() {
|
|
||||||
for (Vault vault : vaults) {
|
|
||||||
if (vault.isUnlocked()) {
|
|
||||||
try {
|
|
||||||
vault.lock(true);
|
|
||||||
} catch (Volume.VolumeException e) {
|
|
||||||
LOG.error("Failed to unmount vault " + vault.getPath(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,28 @@
|
|||||||
package org.cryptomator.ui.unlock;
|
package org.cryptomator.ui.unlock;
|
||||||
|
|
||||||
import dagger.Lazy;
|
import dagger.Lazy;
|
||||||
import javafx.animation.KeyFrame;
|
|
||||||
import javafx.animation.KeyValue;
|
|
||||||
import javafx.animation.Timeline;
|
|
||||||
import javafx.beans.binding.Bindings;
|
import javafx.beans.binding.Bindings;
|
||||||
import javafx.beans.binding.ObjectBinding;
|
import javafx.beans.binding.ObjectBinding;
|
||||||
import javafx.beans.property.BooleanProperty;
|
import javafx.beans.property.BooleanProperty;
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||||
import javafx.beans.property.SimpleBooleanProperty;
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
import javafx.beans.value.WritableValue;
|
import javafx.concurrent.Task;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.Scene;
|
import javafx.scene.Scene;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.control.ContentDisplay;
|
import javafx.scene.control.ContentDisplay;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.util.Duration;
|
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.common.vaults.VaultState;
|
import org.cryptomator.common.vaults.VaultState;
|
||||||
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
import org.cryptomator.cryptolib.api.InvalidPassphraseException;
|
||||||
import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException;
|
|
||||||
import org.cryptomator.keychain.KeychainAccess;
|
import org.cryptomator.keychain.KeychainAccess;
|
||||||
import org.cryptomator.keychain.KeychainAccessException;
|
import org.cryptomator.keychain.KeychainAccessException;
|
||||||
|
import org.cryptomator.ui.common.Animations;
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.common.FxmlFile;
|
import org.cryptomator.ui.common.FxmlFile;
|
||||||
import org.cryptomator.ui.common.FxmlScene;
|
import org.cryptomator.ui.common.FxmlScene;
|
||||||
import org.cryptomator.ui.common.Tasks;
|
import org.cryptomator.ui.common.VaultService;
|
||||||
import org.cryptomator.ui.controls.NiceSecurePasswordField;
|
import org.cryptomator.ui.controls.NiceSecurePasswordField;
|
||||||
import org.cryptomator.ui.forgetPassword.ForgetPasswordComponent;
|
import org.cryptomator.ui.forgetPassword.ForgetPasswordComponent;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -50,22 +46,24 @@ public class UnlockController implements FxController {
|
|||||||
private final ExecutorService executor;
|
private final ExecutorService executor;
|
||||||
private final ObjectBinding<ContentDisplay> unlockButtonState;
|
private final ObjectBinding<ContentDisplay> unlockButtonState;
|
||||||
private final Optional<KeychainAccess> keychainAccess;
|
private final Optional<KeychainAccess> keychainAccess;
|
||||||
|
private final VaultService vaultService;
|
||||||
private final Lazy<Scene> successScene;
|
private final Lazy<Scene> successScene;
|
||||||
private final Lazy<Scene> invalidMountPointScene;
|
private final Lazy<Scene> invalidMountPointScene;
|
||||||
private final Lazy<Scene> genericErrorScene;
|
private final Lazy<Scene> genericErrorScene;
|
||||||
private final ObjectProperty<Exception> genericErrorCause;
|
private final ObjectProperty<Throwable> genericErrorCause;
|
||||||
private final ForgetPasswordComponent.Builder forgetPassword;
|
private final ForgetPasswordComponent.Builder forgetPassword;
|
||||||
private final BooleanProperty unlockButtonDisabled;
|
private final BooleanProperty unlockButtonDisabled;
|
||||||
public NiceSecurePasswordField passwordField;
|
public NiceSecurePasswordField passwordField;
|
||||||
public CheckBox savePassword;
|
public CheckBox savePassword;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public UnlockController(@UnlockWindow Stage window, @UnlockWindow Vault vault, ExecutorService executor, Optional<KeychainAccess> keychainAccess, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, @FxmlScene(FxmlFile.UNLOCK_GENERIC_ERROR) Lazy<Scene> genericErrorScene, @Named("genericErrorCause") ObjectProperty<Exception> genericErrorCause, ForgetPasswordComponent.Builder forgetPassword) {
|
public UnlockController(@UnlockWindow Stage window, @UnlockWindow Vault vault, ExecutorService executor, Optional<KeychainAccess> keychainAccess, VaultService vaultService, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy<Scene> successScene, @FxmlScene(FxmlFile.UNLOCK_INVALID_MOUNT_POINT) Lazy<Scene> invalidMountPointScene, @FxmlScene(FxmlFile.UNLOCK_GENERIC_ERROR) Lazy<Scene> genericErrorScene, @Named("genericErrorCause") ObjectProperty<Throwable> genericErrorCause, ForgetPasswordComponent.Builder forgetPassword) {
|
||||||
this.window = window;
|
this.window = window;
|
||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
this.unlockButtonState = Bindings.createObjectBinding(this::getUnlockButtonState, vault.stateProperty());
|
this.unlockButtonState = Bindings.createObjectBinding(this::getUnlockButtonState, vault.stateProperty());
|
||||||
this.keychainAccess = keychainAccess;
|
this.keychainAccess = keychainAccess;
|
||||||
|
this.vaultService = vaultService;
|
||||||
this.successScene = successScene;
|
this.successScene = successScene;
|
||||||
this.invalidMountPointScene = invalidMountPointScene;
|
this.invalidMountPointScene = invalidMountPointScene;
|
||||||
this.genericErrorScene = genericErrorScene;
|
this.genericErrorScene = genericErrorScene;
|
||||||
@@ -93,36 +91,36 @@ public class UnlockController implements FxController {
|
|||||||
public void unlock() {
|
public void unlock() {
|
||||||
LOG.trace("UnlockController.unlock()");
|
LOG.trace("UnlockController.unlock()");
|
||||||
CharSequence password = passwordField.getCharacters();
|
CharSequence password = passwordField.getCharacters();
|
||||||
vault.setState(VaultState.PROCESSING);
|
|
||||||
Tasks.create(() -> {
|
Task<Vault> task = vaultService.createUnlockTask(vault, password);
|
||||||
vault.unlock(password);
|
task.setOnSucceeded(event -> {
|
||||||
if (keychainAccess.isPresent() && savePassword.isSelected()) {
|
if (keychainAccess.isPresent() && savePassword.isSelected()) {
|
||||||
keychainAccess.get().storePassphrase(vault.getId(), password);
|
try {
|
||||||
|
keychainAccess.get().storePassphrase(vault.getId(), password);
|
||||||
|
} catch (KeychainAccessException e) {
|
||||||
|
LOG.error("Failed to store passphrase in system keychain.", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}).onSuccess(() -> {
|
|
||||||
vault.setState(VaultState.UNLOCKED);
|
|
||||||
passwordField.swipe();
|
passwordField.swipe();
|
||||||
LOG.info("Unlock of '{}' succeeded.", vault.getDisplayableName());
|
LOG.info("Unlock of '{}' succeeded.", vault.getDisplayableName());
|
||||||
window.setScene(successScene.get());
|
window.setScene(successScene.get());
|
||||||
}).onError(InvalidPassphraseException.class, e -> {
|
});
|
||||||
shakeWindow();
|
task.setOnFailed(event -> {
|
||||||
passwordField.selectAll();
|
if (task.getException() instanceof InvalidPassphraseException) {
|
||||||
passwordField.requestFocus();
|
Animations.createShakeWindowAnimation(window).play();
|
||||||
}).onError(NotDirectoryException.class, e -> {
|
passwordField.selectAll();
|
||||||
LOG.error("Unlock failed. Mount point not a directory: {}", e.getMessage());
|
passwordField.requestFocus();
|
||||||
window.setScene(invalidMountPointScene.get());
|
} else if (task.getException() instanceof NotDirectoryException
|
||||||
}).onError(DirectoryNotEmptyException.class, e -> {
|
|| task.getException() instanceof DirectoryNotEmptyException) {
|
||||||
LOG.error("Unlock failed. Mount point not empty: {}", e.getMessage());
|
LOG.error("Unlock failed. Mount point not an empty directory: {}", task.getException().getMessage());
|
||||||
window.setScene(invalidMountPointScene.get());
|
window.setScene(invalidMountPointScene.get());
|
||||||
}).onError(Exception.class, e -> { // including RuntimeExceptions
|
} else {
|
||||||
LOG.error("Unlock failed for technical reasons.", e);
|
LOG.error("Unlock failed for technical reasons.", task.getException());
|
||||||
genericErrorCause.set(e);
|
genericErrorCause.set(task.getException());
|
||||||
window.setScene(genericErrorScene.get());
|
window.setScene(genericErrorScene.get());
|
||||||
}).andFinally(() -> {
|
|
||||||
if (!vault.isUnlocked()) {
|
|
||||||
vault.setState(VaultState.LOCKED);
|
|
||||||
}
|
}
|
||||||
}).runOnce(executor);
|
});
|
||||||
|
executor.execute(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Save Password */
|
/* Save Password */
|
||||||
@@ -167,33 +165,6 @@ public class UnlockController implements FxController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animations */
|
|
||||||
|
|
||||||
private void shakeWindow() {
|
|
||||||
WritableValue<Double> writableWindowX = new WritableValue<>() {
|
|
||||||
@Override
|
|
||||||
public Double getValue() {
|
|
||||||
return window.getX();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setValue(Double value) {
|
|
||||||
window.setX(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Timeline timeline = new Timeline( //
|
|
||||||
new KeyFrame(Duration.ZERO, new KeyValue(writableWindowX, window.getX())), //
|
|
||||||
new KeyFrame(new Duration(100), new KeyValue(writableWindowX, window.getX() - 22.0)), //
|
|
||||||
new KeyFrame(new Duration(200), new KeyValue(writableWindowX, window.getX() + 18.0)), //
|
|
||||||
new KeyFrame(new Duration(300), new KeyValue(writableWindowX, window.getX() - 14.0)), //
|
|
||||||
new KeyFrame(new Duration(400), new KeyValue(writableWindowX, window.getX() + 10.0)), //
|
|
||||||
new KeyFrame(new Duration(500), new KeyValue(writableWindowX, window.getX() - 6.0)), //
|
|
||||||
new KeyFrame(new Duration(600), new KeyValue(writableWindowX, window.getX() + 2.0)), //
|
|
||||||
new KeyFrame(new Duration(700), new KeyValue(writableWindowX, window.getX())) //
|
|
||||||
);
|
|
||||||
timeline.play();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Getter/Setter */
|
/* Getter/Setter */
|
||||||
|
|
||||||
public Vault getVault() {
|
public Vault getVault() {
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import org.cryptomator.ui.forgetPassword.ForgetPasswordComponent;
|
|||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@Module(subcomponents = {ForgetPasswordComponent.class})
|
@Module(subcomponents = {ForgetPasswordComponent.class})
|
||||||
@@ -38,19 +38,19 @@ abstract class UnlockModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@UnlockWindow
|
@UnlockWindow
|
||||||
@UnlockScoped
|
@UnlockScoped
|
||||||
static Stage provideStage(ResourceBundle resourceBundle, @Named("windowIcon") Optional<Image> windowIcon) {
|
static Stage provideStage(ResourceBundle resourceBundle, @Named("windowIcons") List<Image> windowIcons) {
|
||||||
Stage stage = new Stage();
|
Stage stage = new Stage();
|
||||||
stage.setTitle(resourceBundle.getString("unlock.title"));
|
stage.setTitle(resourceBundle.getString("unlock.title"));
|
||||||
stage.setResizable(false);
|
stage.setResizable(false);
|
||||||
stage.initModality(Modality.APPLICATION_MODAL);
|
stage.initModality(Modality.APPLICATION_MODAL);
|
||||||
windowIcon.ifPresent(stage.getIcons()::add);
|
stage.getIcons().addAll(windowIcons);
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Named("genericErrorCause")
|
@Named("genericErrorCause")
|
||||||
@UnlockScoped
|
@UnlockScoped
|
||||||
static ObjectProperty<Exception> provideGenericErrorCause() {
|
static ObjectProperty<Throwable> provideGenericErrorCause() {
|
||||||
return new SimpleObjectProperty<>();
|
return new SimpleObjectProperty<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ abstract class UnlockModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@IntoMap
|
@IntoMap
|
||||||
@FxControllerKey(StackTraceController.class)
|
@FxControllerKey(StackTraceController.class)
|
||||||
static FxController provideStackTraceController(@Named("genericErrorCause") ObjectProperty<Exception> errorCause) {
|
static FxController provideStackTraceController(@Named("genericErrorCause") ObjectProperty<Throwable> errorCause) {
|
||||||
return new StackTraceController(errorCause.get());
|
return new StackTraceController(errorCause.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-18
@@ -1,11 +1,9 @@
|
|||||||
package org.cryptomator.ui.vaultoptions;
|
package org.cryptomator.ui.vaultoptions;
|
||||||
|
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.stage.Stage;
|
import javafx.scene.control.CheckBox;
|
||||||
import org.cryptomator.common.vaults.Vault;
|
import org.cryptomator.common.vaults.Vault;
|
||||||
import org.cryptomator.ui.changepassword.ChangePasswordComponent;
|
|
||||||
import org.cryptomator.ui.common.FxController;
|
import org.cryptomator.ui.common.FxController;
|
||||||
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@@ -13,26 +11,15 @@ import javax.inject.Inject;
|
|||||||
public class GeneralVaultOptionsController implements FxController {
|
public class GeneralVaultOptionsController implements FxController {
|
||||||
|
|
||||||
private final Vault vault;
|
private final Vault vault;
|
||||||
private final Stage window;
|
public CheckBox unlockOnStartupCheckbox;
|
||||||
private final ChangePasswordComponent.Builder changePasswordWindow;
|
|
||||||
private final RecoveryKeyComponent.Builder recoveryKeyWindow;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
GeneralVaultOptionsController(@VaultOptionsWindow Vault vault, @VaultOptionsWindow Stage window, ChangePasswordComponent.Builder changePasswordWindow, RecoveryKeyComponent.Builder recoveryKeyWindow) {
|
GeneralVaultOptionsController(@VaultOptionsWindow Vault vault) {
|
||||||
this.vault = vault;
|
this.vault = vault;
|
||||||
this.window = window;
|
|
||||||
this.changePasswordWindow = changePasswordWindow;
|
|
||||||
this.recoveryKeyWindow = recoveryKeyWindow;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
public void changePassword() {
|
public void initialize() {
|
||||||
changePasswordWindow.vault(vault).owner(window).build().showChangePasswordWindow();
|
unlockOnStartupCheckbox.selectedProperty().bindBidirectional(vault.getVaultSettings().unlockAfterStartup());
|
||||||
}
|
}
|
||||||
|
|
||||||
@FXML
|
|
||||||
public void showRecoveryKey() {
|
|
||||||
recoveryKeyWindow.vault(vault).owner(window).build().showRecoveryKeyCreationWindow();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package org.cryptomator.ui.vaultoptions;
|
||||||
|
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.cryptomator.common.vaults.Vault;
|
||||||
|
import org.cryptomator.ui.changepassword.ChangePasswordComponent;
|
||||||
|
import org.cryptomator.ui.common.FxController;
|
||||||
|
import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
@VaultOptionsScoped
|
||||||
|
public class MasterkeyOptionsController implements FxController {
|
||||||
|
|
||||||
|
private final Vault vault;
|
||||||
|
private final Stage window;
|
||||||
|
private final ChangePasswordComponent.Builder changePasswordWindow;
|
||||||
|
private final RecoveryKeyComponent.Builder recoveryKeyWindow;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
MasterkeyOptionsController(@VaultOptionsWindow Vault vault, @VaultOptionsWindow Stage window, ChangePasswordComponent.Builder changePasswordWindow, RecoveryKeyComponent.Builder recoveryKeyWindow) {
|
||||||
|
this.vault = vault;
|
||||||
|
this.window = window;
|
||||||
|
this.changePasswordWindow = changePasswordWindow;
|
||||||
|
this.recoveryKeyWindow = recoveryKeyWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void changePassword() {
|
||||||
|
changePasswordWindow.vault(vault).owner(window).build().showChangePasswordWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void showRecoveryKey() {
|
||||||
|
recoveryKeyWindow.vault(vault).owner(window).build().showRecoveryKeyCreationWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void showRecoverVaultDialogue() {
|
||||||
|
recoveryKeyWindow.vault(vault).owner(window).build().showRecoveryKeyRecoverWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,8 +21,8 @@ import org.cryptomator.ui.recoverykey.RecoveryKeyComponent;
|
|||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@Module(subcomponents = {ChangePasswordComponent.class, RecoveryKeyComponent.class})
|
@Module(subcomponents = {ChangePasswordComponent.class, RecoveryKeyComponent.class})
|
||||||
@@ -38,13 +38,15 @@ abstract class VaultOptionsModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@VaultOptionsWindow
|
@VaultOptionsWindow
|
||||||
@VaultOptionsScoped
|
@VaultOptionsScoped
|
||||||
static Stage provideStage(@MainWindow Stage owner, @VaultOptionsWindow Vault vault, ResourceBundle resourceBundle, @Named("windowIcon") Optional<Image> windowIcon) {
|
static Stage provideStage(@MainWindow Stage owner, @VaultOptionsWindow Vault vault, ResourceBundle resourceBundle, @Named("windowIcons") List<Image> windowIcons) {
|
||||||
Stage stage = new Stage();
|
Stage stage = new Stage();
|
||||||
stage.setTitle(vault.getDisplayableName());
|
stage.setTitle(vault.getDisplayableName());
|
||||||
stage.setResizable(false);
|
stage.setResizable(true);
|
||||||
|
stage.setMinWidth(400);
|
||||||
|
stage.setMinHeight(300);
|
||||||
stage.initModality(Modality.WINDOW_MODAL);
|
stage.initModality(Modality.WINDOW_MODAL);
|
||||||
stage.initOwner(owner);
|
stage.initOwner(owner);
|
||||||
windowIcon.ifPresent(stage.getIcons()::add);
|
stage.getIcons().addAll(windowIcons);
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,4 +74,9 @@ abstract class VaultOptionsModule {
|
|||||||
@FxControllerKey(MountOptionsController.class)
|
@FxControllerKey(MountOptionsController.class)
|
||||||
abstract FxController bindMountOptionsController(MountOptionsController controller);
|
abstract FxController bindMountOptionsController(MountOptionsController controller);
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@IntoMap
|
||||||
|
@FxControllerKey(MasterkeyOptionsController.class)
|
||||||
|
abstract FxController bindMasterkeyOptionsController(MasterkeyOptionsController controller);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ import org.cryptomator.ui.common.FxmlScene;
|
|||||||
|
|
||||||
import javax.inject.Named;
|
import javax.inject.Named;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
@@ -34,12 +34,12 @@ abstract class WrongFileAlertModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@WrongFileAlertWindow
|
@WrongFileAlertWindow
|
||||||
@WrongFileAlertScoped
|
@WrongFileAlertScoped
|
||||||
static Stage provideStage(ResourceBundle resourceBundle, @Named("windowIcon") Optional<Image> windowIcon) {
|
static Stage provideStage(ResourceBundle resourceBundle, @Named("windowIcons") List<Image> windowIcons) {
|
||||||
Stage stage = new Stage();
|
Stage stage = new Stage();
|
||||||
stage.setTitle(resourceBundle.getString("wrongFileAlert.title"));
|
stage.setTitle(resourceBundle.getString("wrongFileAlert.title"));
|
||||||
stage.setResizable(false);
|
stage.setResizable(false);
|
||||||
stage.initModality(Modality.WINDOW_MODAL);
|
stage.initModality(Modality.WINDOW_MODAL);
|
||||||
windowIcon.ifPresent(stage.getIcons()::add);
|
stage.getIcons().addAll(windowIcons);
|
||||||
return stage;
|
return stage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 8.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 34 KiB |
@@ -17,7 +17,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
src: url('dosis-bold.ttf');
|
src: url('quicksand-bold.ttf');
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -27,39 +27,37 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
GREEN_0: #373B30;
|
PRIMARY_D2: #2D4D2E;
|
||||||
GREEN_1: #384D14;
|
PRIMARY_D1: #407F41;
|
||||||
GREEN_2: #476611;
|
PRIMARY: #49B04A;
|
||||||
GREEN_3: #598016;
|
PRIMARY_L1: #66CC68;
|
||||||
GREEN_4: #699917;
|
PRIMARY_L2: #EBF5EB;
|
||||||
GREEN_5: #79B01A;
|
|
||||||
GREEN_6: #91C734;
|
|
||||||
GREEN_7: #B9E070;
|
|
||||||
GREEN_8: #D7E7BA;
|
|
||||||
GREEN_9: #F3F5F0;
|
|
||||||
|
|
||||||
GRAY_0: #222222;
|
SECONDARY: #008A7B;
|
||||||
GRAY_1: #3B3B3B;
|
|
||||||
GRAY_2: #515151;
|
GRAY_0: #1F2122;
|
||||||
GRAY_3: #626262;
|
GRAY_1: #35393B;
|
||||||
GRAY_4: #7E7E7E;
|
GRAY_2: #494E51;
|
||||||
GRAY_5: #9E9E9E;
|
GRAY_3: #585E62;
|
||||||
GRAY_6: #B1B1B1;
|
GRAY_4: #71797E;
|
||||||
GRAY_7: #CFCFCF;
|
GRAY_5: #8E989E;
|
||||||
GRAY_8: #E1E1E1;
|
GRAY_6: #9FAAB1;
|
||||||
GRAY_9: #F7F7F7;
|
GRAY_7: #BEC9CF;
|
||||||
|
GRAY_8: #D3DCE1;
|
||||||
|
GRAY_9: #EDF3F7;
|
||||||
|
|
||||||
RED_5: #E74C3C;
|
RED_5: #E74C3C;
|
||||||
ORANGE_5: #E67E22;
|
ORANGE_5: #E67E22;
|
||||||
YELLOW_5: #F1C40F;
|
YELLOW_5: #F1C40F;
|
||||||
|
|
||||||
PRIMARY_BG: GREEN_3;
|
|
||||||
SECONDARY_BG: GRAY_3;
|
|
||||||
MAIN_BG: GRAY_1;
|
MAIN_BG: GRAY_1;
|
||||||
TEXT_FILL: GRAY_9;
|
TEXT_FILL: GRAY_9;
|
||||||
TEXT_FILL_PRIMARY: GREEN_5;
|
TEXT_FILL_HIGHLIGHTED: PRIMARY;
|
||||||
TEXT_FILL_SECONDARY: GRAY_5;
|
TEXT_FILL_MUTED: GRAY_5;
|
||||||
TEXT_FILL_WHITE: white;
|
|
||||||
|
TITLE_BG: linear-gradient(to bottom, GRAY_2, GRAY_1);
|
||||||
|
TITLE_TEXT_FILL: PRIMARY;
|
||||||
|
|
||||||
CONTROL_BORDER_NORMAL: GRAY_3;
|
CONTROL_BORDER_NORMAL: GRAY_3;
|
||||||
CONTROL_BORDER_FOCUSED: GRAY_5;
|
CONTROL_BORDER_FOCUSED: GRAY_5;
|
||||||
CONTROL_BORDER_DISABLED: GRAY_2;
|
CONTROL_BORDER_DISABLED: GRAY_2;
|
||||||
@@ -67,27 +65,20 @@
|
|||||||
CONTROL_BG_HOVER: GRAY_1;
|
CONTROL_BG_HOVER: GRAY_1;
|
||||||
CONTROL_BG_ARMED: GRAY_2;
|
CONTROL_BG_ARMED: GRAY_2;
|
||||||
CONTROL_BG_DISABLED: GRAY_1;
|
CONTROL_BG_DISABLED: GRAY_1;
|
||||||
CONTROL_PRIMARY_BORDER_NORMAL: GREEN_5;
|
CONTROL_BG_SELECTED: GRAY_1;
|
||||||
CONTROL_PRIMARY_BORDER_FOCUSED: GREEN_7;
|
|
||||||
CONTROL_PRIMARY_BORDER_DISABLED: GREEN_3;
|
CONTROL_PRIMARY_BORDER_NORMAL: PRIMARY;
|
||||||
CONTROL_PRIMARY_BG_NORMAL: GREEN_3;
|
CONTROL_PRIMARY_BORDER_ARMED: PRIMARY_L1;
|
||||||
CONTROL_PRIMARY_BG_ARMED: GREEN_4;
|
CONTROL_PRIMARY_BORDER_FOCUSED: SECONDARY;
|
||||||
CONTROL_PRIMARY_BG_DISABLED: GREEN_2;
|
CONTROL_PRIMARY_BG_NORMAL: PRIMARY;
|
||||||
CONTROL_PRIMARY_LIGHT_BG_NORMAL: GREEN_0;
|
CONTROL_PRIMARY_BG_ARMED: PRIMARY_L1;
|
||||||
CONTROL_WHITE_BG_ARMED: GRAY_8;
|
|
||||||
SCROLL_BAR_THUMB_NORMAL: GRAY_3;
|
SCROLL_BAR_THUMB_NORMAL: GRAY_3;
|
||||||
SCROLL_BAR_THUMB_HOVER: GRAY_4;
|
SCROLL_BAR_THUMB_HOVER: GRAY_4;
|
||||||
INDICATOR_BG: RED_5;
|
|
||||||
DRAG_N_DROP_INDICATOR_BG: GRAY_3;
|
|
||||||
PROGRESS_INDICATOR_BEGIN: GRAY_7;
|
PROGRESS_INDICATOR_BEGIN: GRAY_7;
|
||||||
PROGRESS_INDICATOR_END: GRAY_5;
|
PROGRESS_INDICATOR_END: GRAY_5;
|
||||||
PROGRESS_BAR_BG: GRAY_2;
|
PROGRESS_BAR_BG: GRAY_2;
|
||||||
PASSWORD_STRENGTH_INDICATOR_BG: GRAY_3;
|
|
||||||
PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_0: RED_5;
|
|
||||||
PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_1: ORANGE_5;
|
|
||||||
PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_2: YELLOW_5;
|
|
||||||
PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_3: GREEN_6;
|
|
||||||
PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_4: GREEN_5;
|
|
||||||
|
|
||||||
-fx-background-color: MAIN_BG;
|
-fx-background-color: MAIN_BG;
|
||||||
-fx-text-fill: TEXT_FILL;
|
-fx-text-fill: TEXT_FILL;
|
||||||
@@ -105,7 +96,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.label-secondary {
|
.label-secondary {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label-large {
|
.label-large {
|
||||||
@@ -136,11 +127,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.glyph-icon-primary {
|
.glyph-icon-primary {
|
||||||
-fx-fill: PRIMARY_BG;
|
-fx-fill: PRIMARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
.glyph-icon-secondary {
|
.glyph-icon-muted {
|
||||||
-fx-fill: SECONDARY_BG;
|
-fx-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.glyph-icon-white {
|
.glyph-icon-white {
|
||||||
@@ -158,15 +149,16 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
.main-window .title {
|
.main-window .title {
|
||||||
-fx-background-color: PRIMARY_BG;
|
-fx-background-color: CONTROL_BORDER_NORMAL, TITLE_BG;
|
||||||
|
-fx-background-insets: 0, 0 0 1px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .title .label {
|
.main-window .title .label {
|
||||||
-fx-font-family: 'Dosis';
|
-fx-font-family: 'Quicksand';
|
||||||
-fx-font-size: 21px;
|
-fx-font-size: 16px;
|
||||||
-fx-font-style: normal;
|
-fx-font-style: normal;
|
||||||
-fx-font-weight: 700;
|
-fx-font-weight: 700;
|
||||||
-fx-text-fill: white;
|
-fx-text-fill: TITLE_TEXT_FILL;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .title .button {
|
.main-window .title .button {
|
||||||
@@ -181,24 +173,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.main-window .title .button:armed .glyph-icon {
|
.main-window .title .button:armed .glyph-icon {
|
||||||
-fx-fill: CONTROL_WHITE_BG_ARMED;
|
-fx-fill: GRAY_8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .update-indicator {
|
.main-window .update-indicator {
|
||||||
-fx-background-color: PRIMARY_BG, white, INDICATOR_BG;
|
-fx-background-color: white, RED_5;
|
||||||
-fx-background-insets: 0, 1px, 2px;
|
-fx-background-insets: 1px, 2px;
|
||||||
-fx-background-radius: 6px, 5px, 4px;
|
-fx-background-radius: 6px, 5px;
|
||||||
-fx-translate-x: -1px;
|
-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.8), 2, 0, 0, 0);
|
||||||
-fx-translate-y: 1px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .drag-n-drop-indicator {
|
.main-window .drag-n-drop-indicator {
|
||||||
-fx-border-color: DRAG_N_DROP_INDICATOR_BG;
|
-fx-border-color: SECONDARY;
|
||||||
-fx-border-width: 3px;
|
-fx-border-width: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .drag-n-drop-indicator .drag-n-drop-header {
|
.main-window .drag-n-drop-indicator .drag-n-drop-header {
|
||||||
-fx-background-color: DRAG_N_DROP_INDICATOR_BG;
|
-fx-background-color: SECONDARY;
|
||||||
-fx-padding: 3px;
|
-fx-padding: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,24 +216,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab-pane .tab:selected {
|
.tab-pane .tab:selected {
|
||||||
-fx-background-color: PRIMARY_BG, CONTROL_PRIMARY_LIGHT_BG_NORMAL;
|
-fx-background-color: PRIMARY, CONTROL_BG_SELECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-pane .tab .tab-label {
|
.tab-pane .tab .tab-label {
|
||||||
-fx-text-fill: SECONDARY_BG;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
-fx-alignment: CENTER;
|
-fx-alignment: CENTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-pane .tab .glyph-icon {
|
.tab-pane .tab .glyph-icon {
|
||||||
-fx-fill: SECONDARY_BG;
|
-fx-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-pane .tab:selected .glyph-icon {
|
.tab-pane .tab:selected .glyph-icon {
|
||||||
-fx-fill: PRIMARY_BG;
|
-fx-fill: PRIMARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-pane .tab:selected .tab-label {
|
.tab-pane .tab:selected .tab-label {
|
||||||
-fx-text-fill: TEXT_FILL_PRIMARY;
|
-fx-text-fill: TEXT_FILL_HIGHLIGHTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -271,16 +262,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.list-view:focused .list-cell:selected {
|
.list-view:focused .list-cell:selected {
|
||||||
-fx-background-color: PRIMARY_BG, CONTROL_PRIMARY_LIGHT_BG_NORMAL;
|
-fx-background-color: PRIMARY, CONTROL_BG_SELECTED;
|
||||||
-fx-background-insets: 0, 0 0 0 3px;
|
-fx-background-insets: 0, 0 0 0 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-cell:selected {
|
.list-cell:selected {
|
||||||
-fx-background-color: CONTROL_PRIMARY_LIGHT_BG_NORMAL;
|
-fx-background-color: CONTROL_BG_SELECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-cell .glyph-icon {
|
.list-cell .glyph-icon {
|
||||||
-fx-fill: SECONDARY_BG;
|
-fx-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-cell .header-label {
|
.list-cell .header-label {
|
||||||
@@ -289,16 +280,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.list-cell .detail-label {
|
.list-cell .detail-label {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
-fx-font-size: 0.8em;
|
-fx-font-size: 0.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-cell:selected .glyph-icon {
|
.list-cell:selected .glyph-icon {
|
||||||
-fx-fill: PRIMARY_BG;
|
-fx-fill: PRIMARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-cell:selected .header-label {
|
.list-cell:selected .header-label {
|
||||||
-fx-text-fill: TEXT_FILL_PRIMARY;
|
-fx-text-fill: TEXT_FILL_HIGHLIGHTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-cell.drop-above {
|
.list-cell.drop-above {
|
||||||
@@ -379,13 +370,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.badge-primary {
|
.badge-primary {
|
||||||
-fx-text-fill: TEXT_FILL_WHITE;
|
-fx-text-fill: white;
|
||||||
-fx-background-color: PRIMARY_BG;
|
-fx-background-color: PRIMARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-secondary {
|
.badge-secondary {
|
||||||
-fx-text-fill: TEXT_FILL_WHITE;
|
-fx-text-fill: white;
|
||||||
-fx-background-color: SECONDARY_BG;
|
-fx-background-color: SECONDARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -395,27 +386,27 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
.password-strength-indicator .segment {
|
.password-strength-indicator .segment {
|
||||||
-fx-background-color: PASSWORD_STRENGTH_INDICATOR_BG;
|
-fx-background-color: CONTROL_BORDER_NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-strength-indicator.strength-0 .segment.active {
|
.password-strength-indicator.strength-0 .segment.active {
|
||||||
-fx-background-color: PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_0;
|
-fx-background-color: RED_5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-strength-indicator.strength-1 .segment.active {
|
.password-strength-indicator.strength-1 .segment.active {
|
||||||
-fx-background-color: PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_1;
|
-fx-background-color: ORANGE_5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-strength-indicator.strength-2 .segment.active {
|
.password-strength-indicator.strength-2 .segment.active {
|
||||||
-fx-background-color: PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_2;
|
-fx-background-color: YELLOW_5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-strength-indicator.strength-3 .segment.active {
|
.password-strength-indicator.strength-3 .segment.active {
|
||||||
-fx-background-color: PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_3;
|
-fx-background-color: PRIMARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-strength-indicator.strength-4 .segment.active {
|
.password-strength-indicator.strength-4 .segment.active {
|
||||||
-fx-background-color: PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_4;
|
-fx-background-color: PRIMARY_D1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -442,8 +433,8 @@
|
|||||||
.text-input {
|
.text-input {
|
||||||
-fx-cursor: text;
|
-fx-cursor: text;
|
||||||
-fx-text-fill: TEXT_FILL;
|
-fx-text-fill: TEXT_FILL;
|
||||||
-fx-highlight-fill: PRIMARY_BG;
|
-fx-highlight-fill: PRIMARY;
|
||||||
-fx-prompt-text-fill: TEXT_FILL_SECONDARY;
|
-fx-prompt-text-fill: TEXT_FILL_MUTED;
|
||||||
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_NORMAL;
|
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_NORMAL;
|
||||||
-fx-background-insets: 0, 1px;
|
-fx-background-insets: 0, 1px;
|
||||||
-fx-background-radius: 4px;
|
-fx-background-radius: 4px;
|
||||||
@@ -455,7 +446,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.text-input:disabled {
|
.text-input:disabled {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
-fx-background-color: CONTROL_BORDER_DISABLED, CONTROL_BG_DISABLED;
|
-fx-background-color: CONTROL_BORDER_DISABLED, CONTROL_BG_DISABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,7 +478,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.text-input:disabled {
|
.text-input:disabled {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
-fx-background-color: CONTROL_BORDER_DISABLED, CONTROL_BG_DISABLED;
|
-fx-background-color: CONTROL_BORDER_DISABLED, CONTROL_BG_DISABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,8 +490,8 @@
|
|||||||
-fx-padding: 0.2em 0.5em 0.2em 0.5em;
|
-fx-padding: 0.2em 0.5em 0.2em 0.5em;
|
||||||
-fx-cursor: text;
|
-fx-cursor: text;
|
||||||
-fx-text-fill: TEXT_FILL;
|
-fx-text-fill: TEXT_FILL;
|
||||||
-fx-highlight-fill: PRIMARY_BG;
|
-fx-highlight-fill: PRIMARY;
|
||||||
-fx-prompt-text-fill: TEXT_FILL_SECONDARY;
|
-fx-prompt-text-fill: TEXT_FILL_MUTED;
|
||||||
-fx-background-color: null;
|
-fx-background-color: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,7 +520,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.button:default {
|
.button:default {
|
||||||
-fx-text-fill: TEXT_FILL_WHITE;
|
-fx-text-fill: white;
|
||||||
-fx-background-color: CONTROL_PRIMARY_BORDER_NORMAL, CONTROL_PRIMARY_BG_NORMAL;
|
-fx-background-color: CONTROL_PRIMARY_BORDER_NORMAL, CONTROL_PRIMARY_BG_NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,25 +529,25 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.button:default:armed {
|
.button:default:armed {
|
||||||
-fx-background-color: CONTROL_PRIMARY_BORDER_NORMAL, CONTROL_PRIMARY_BG_ARMED;
|
-fx-background-color: CONTROL_PRIMARY_BORDER_ARMED, CONTROL_PRIMARY_BG_ARMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:disabled,
|
.button:disabled,
|
||||||
.button:default:disabled {
|
.button:default:disabled {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
-fx-background-color: CONTROL_BORDER_DISABLED, CONTROL_BG_DISABLED;
|
-fx-background-color: CONTROL_BORDER_DISABLED, CONTROL_BG_DISABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:disabled .glyph-icon {
|
.button:disabled .glyph-icon {
|
||||||
-fx-fill: TEXT_FILL_SECONDARY;
|
-fx-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:default .glyph-icon {
|
.button:default .glyph-icon {
|
||||||
-fx-fill: TEXT_FILL_WHITE;
|
-fx-fill: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:default .label {
|
.button:default .label {
|
||||||
-fx-text-fill: TEXT_FILL_WHITE;
|
-fx-text-fill: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-large {
|
.button-large {
|
||||||
@@ -577,7 +568,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hyperlink.hyperlink-secondary {
|
.hyperlink.hyperlink-secondary {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hyperlink-hover-icon {
|
.hyperlink-hover-icon {
|
||||||
@@ -601,7 +592,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.check-box:disabled {
|
.check-box:disabled {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.check-box > .box {
|
.check-box > .box {
|
||||||
@@ -694,7 +685,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.choice-box:disabled > .label {
|
.choice-box:disabled > .label {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.choice-box > .open-button {
|
.choice-box > .open-button {
|
||||||
@@ -709,7 +700,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.choice-box:disabled > .open-button > .arrow {
|
.choice-box:disabled > .open-button > .arrow {
|
||||||
-fx-background-color: transparent, TEXT_FILL_SECONDARY;
|
-fx-background-color: transparent, TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.choice-box .context-menu {
|
.choice-box .context-menu {
|
||||||
@@ -758,7 +749,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-item:disabled > .label {
|
.menu-item:disabled > .label {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio-menu-item:checked > .left-container > .radio {
|
.radio-menu-item:checked > .left-container > .radio {
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -17,7 +17,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
src: url('dosis-bold.ttf');
|
src: url('quicksand-bold.ttf');
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -27,16 +27,13 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
GREEN_0: #373B30;
|
PRIMARY_D2: #2D4D2E;
|
||||||
GREEN_1: #384D14;
|
PRIMARY_D1: #407F41;
|
||||||
GREEN_2: #476611;
|
PRIMARY: #49B04A;
|
||||||
GREEN_3: #598016;
|
PRIMARY_L1: #66CC68;
|
||||||
GREEN_4: #699917;
|
PRIMARY_L2: #EBF5EB;
|
||||||
GREEN_5: #79B01A;
|
|
||||||
GREEN_6: #91C734;
|
SECONDARY: #008A7B;
|
||||||
GREEN_7: #B9E070;
|
|
||||||
GREEN_8: #D7E7BA;
|
|
||||||
GREEN_9: #F3F5F0;
|
|
||||||
|
|
||||||
GRAY_0: #222222;
|
GRAY_0: #222222;
|
||||||
GRAY_1: #3B3B3B;
|
GRAY_1: #3B3B3B;
|
||||||
@@ -53,13 +50,14 @@
|
|||||||
ORANGE_5: #E67E22;
|
ORANGE_5: #E67E22;
|
||||||
YELLOW_5: #F1C40F;
|
YELLOW_5: #F1C40F;
|
||||||
|
|
||||||
PRIMARY_BG: GREEN_5;
|
|
||||||
SECONDARY_BG: GRAY_5;
|
|
||||||
MAIN_BG: GRAY_9;
|
MAIN_BG: GRAY_9;
|
||||||
TEXT_FILL: GRAY_0;
|
TEXT_FILL: GRAY_0;
|
||||||
TEXT_FILL_PRIMARY: GREEN_4;
|
TEXT_FILL_HIGHLIGHTED: PRIMARY;
|
||||||
TEXT_FILL_SECONDARY: GRAY_4;
|
TEXT_FILL_MUTED: GRAY_5;
|
||||||
TEXT_FILL_WHITE: white;
|
|
||||||
|
TITLE_BG: PRIMARY;
|
||||||
|
TITLE_TEXT_FILL: white;
|
||||||
|
|
||||||
CONTROL_BORDER_NORMAL: GRAY_7;
|
CONTROL_BORDER_NORMAL: GRAY_7;
|
||||||
CONTROL_BORDER_FOCUSED: GRAY_5;
|
CONTROL_BORDER_FOCUSED: GRAY_5;
|
||||||
CONTROL_BORDER_DISABLED: GRAY_8;
|
CONTROL_BORDER_DISABLED: GRAY_8;
|
||||||
@@ -67,27 +65,20 @@
|
|||||||
CONTROL_BG_HOVER: GRAY_9;
|
CONTROL_BG_HOVER: GRAY_9;
|
||||||
CONTROL_BG_ARMED: GRAY_8;
|
CONTROL_BG_ARMED: GRAY_8;
|
||||||
CONTROL_BG_DISABLED: GRAY_9;
|
CONTROL_BG_DISABLED: GRAY_9;
|
||||||
CONTROL_PRIMARY_BORDER_NORMAL: GREEN_3;
|
CONTROL_BG_SELECTED: PRIMARY_L2;
|
||||||
CONTROL_PRIMARY_BORDER_FOCUSED: GREEN_1;
|
|
||||||
CONTROL_PRIMARY_BORDER_DISABLED: GREEN_5;
|
CONTROL_PRIMARY_BORDER_NORMAL: PRIMARY_D1;
|
||||||
CONTROL_PRIMARY_BG_NORMAL: GREEN_5;
|
CONTROL_PRIMARY_BORDER_ARMED: PRIMARY_D2;
|
||||||
CONTROL_PRIMARY_BG_ARMED: GREEN_4;
|
CONTROL_PRIMARY_BORDER_FOCUSED: SECONDARY;
|
||||||
CONTROL_PRIMARY_BG_DISABLED: GREEN_6;
|
CONTROL_PRIMARY_BG_NORMAL: PRIMARY;
|
||||||
CONTROL_PRIMARY_LIGHT_BG_NORMAL: GREEN_9;
|
CONTROL_PRIMARY_BG_ARMED: PRIMARY_D1;
|
||||||
CONTROL_WHITE_BG_ARMED: GRAY_8;
|
|
||||||
SCROLL_BAR_THUMB_NORMAL: GRAY_7;
|
SCROLL_BAR_THUMB_NORMAL: GRAY_7;
|
||||||
SCROLL_BAR_THUMB_HOVER: GRAY_6;
|
SCROLL_BAR_THUMB_HOVER: GRAY_6;
|
||||||
INDICATOR_BG: RED_5;
|
|
||||||
DRAG_N_DROP_INDICATOR_BG: GRAY_5;
|
|
||||||
PROGRESS_INDICATOR_BEGIN: GRAY_2;
|
PROGRESS_INDICATOR_BEGIN: GRAY_2;
|
||||||
PROGRESS_INDICATOR_END: GRAY_4;
|
PROGRESS_INDICATOR_END: GRAY_4;
|
||||||
PROGRESS_BAR_BG: GRAY_8;
|
PROGRESS_BAR_BG: GRAY_8;
|
||||||
PASSWORD_STRENGTH_INDICATOR_BG: GRAY_5;
|
|
||||||
PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_0: RED_5;
|
|
||||||
PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_1: ORANGE_5;
|
|
||||||
PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_2: YELLOW_5;
|
|
||||||
PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_3: GREEN_6;
|
|
||||||
PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_4: GREEN_5;
|
|
||||||
|
|
||||||
-fx-background-color: MAIN_BG;
|
-fx-background-color: MAIN_BG;
|
||||||
-fx-text-fill: TEXT_FILL;
|
-fx-text-fill: TEXT_FILL;
|
||||||
@@ -105,7 +96,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.label-secondary {
|
.label-secondary {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.label-large {
|
.label-large {
|
||||||
@@ -136,11 +127,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.glyph-icon-primary {
|
.glyph-icon-primary {
|
||||||
-fx-fill: PRIMARY_BG;
|
-fx-fill: PRIMARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
.glyph-icon-secondary {
|
.glyph-icon-muted {
|
||||||
-fx-fill: SECONDARY_BG;
|
-fx-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.glyph-icon-white {
|
.glyph-icon-white {
|
||||||
@@ -158,15 +149,15 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
.main-window .title {
|
.main-window .title {
|
||||||
-fx-background-color: PRIMARY_BG;
|
-fx-background-color: TITLE_BG;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .title .label {
|
.main-window .title .label {
|
||||||
-fx-font-family: 'Dosis';
|
-fx-font-family: 'Quicksand';
|
||||||
-fx-font-size: 21px;
|
-fx-font-size: 16px;
|
||||||
-fx-font-style: normal;
|
-fx-font-style: normal;
|
||||||
-fx-font-weight: 700;
|
-fx-font-weight: 700;
|
||||||
-fx-text-fill: white;
|
-fx-text-fill: TITLE_TEXT_FILL;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .title .button {
|
.main-window .title .button {
|
||||||
@@ -181,24 +172,23 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.main-window .title .button:armed .glyph-icon {
|
.main-window .title .button:armed .glyph-icon {
|
||||||
-fx-fill: CONTROL_WHITE_BG_ARMED;
|
-fx-fill: GRAY_8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .update-indicator {
|
.main-window .update-indicator {
|
||||||
-fx-background-color: PRIMARY_BG, white, INDICATOR_BG;
|
-fx-background-color: white, RED_5;
|
||||||
-fx-background-insets: 0, 1px, 2px;
|
-fx-background-insets: 1px, 2px;
|
||||||
-fx-background-radius: 6px, 5px, 4px;
|
-fx-background-radius: 6px, 5px;
|
||||||
-fx-translate-x: -1px;
|
-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.8), 2, 0, 0, 0);
|
||||||
-fx-translate-y: 1px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .drag-n-drop-indicator {
|
.main-window .drag-n-drop-indicator {
|
||||||
-fx-border-color: DRAG_N_DROP_INDICATOR_BG;
|
-fx-border-color: SECONDARY;
|
||||||
-fx-border-width: 3px;
|
-fx-border-width: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-window .drag-n-drop-indicator .drag-n-drop-header {
|
.main-window .drag-n-drop-indicator .drag-n-drop-header {
|
||||||
-fx-background-color: DRAG_N_DROP_INDICATOR_BG;
|
-fx-background-color: SECONDARY;
|
||||||
-fx-padding: 3px;
|
-fx-padding: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,24 +215,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tab-pane .tab:selected {
|
.tab-pane .tab:selected {
|
||||||
-fx-background-color: PRIMARY_BG, CONTROL_PRIMARY_LIGHT_BG_NORMAL;
|
-fx-background-color: PRIMARY, CONTROL_BG_SELECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-pane .tab .tab-label {
|
.tab-pane .tab .tab-label {
|
||||||
-fx-text-fill: SECONDARY_BG;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
-fx-alignment: CENTER;
|
-fx-alignment: CENTER;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-pane .tab .glyph-icon {
|
.tab-pane .tab .glyph-icon {
|
||||||
-fx-fill: SECONDARY_BG;
|
-fx-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-pane .tab:selected .glyph-icon {
|
.tab-pane .tab:selected .glyph-icon {
|
||||||
-fx-fill: PRIMARY_BG;
|
-fx-fill: PRIMARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-pane .tab:selected .tab-label {
|
.tab-pane .tab:selected .tab-label {
|
||||||
-fx-text-fill: TEXT_FILL_PRIMARY;
|
-fx-text-fill: TEXT_FILL_HIGHLIGHTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -271,16 +261,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.list-view:focused .list-cell:selected {
|
.list-view:focused .list-cell:selected {
|
||||||
-fx-background-color: PRIMARY_BG, CONTROL_PRIMARY_LIGHT_BG_NORMAL;
|
-fx-background-color: PRIMARY, CONTROL_BG_SELECTED;
|
||||||
-fx-background-insets: 0, 0 0 0 3px;
|
-fx-background-insets: 0, 0 0 0 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-cell:selected {
|
.list-cell:selected {
|
||||||
-fx-background-color: CONTROL_PRIMARY_LIGHT_BG_NORMAL;
|
-fx-background-color: CONTROL_BG_SELECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-cell .glyph-icon {
|
.list-cell .glyph-icon {
|
||||||
-fx-fill: SECONDARY_BG;
|
-fx-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-cell .header-label {
|
.list-cell .header-label {
|
||||||
@@ -289,16 +279,16 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.list-cell .detail-label {
|
.list-cell .detail-label {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
-fx-font-size: 0.8em;
|
-fx-font-size: 0.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-cell:selected .glyph-icon {
|
.list-cell:selected .glyph-icon {
|
||||||
-fx-fill: PRIMARY_BG;
|
-fx-fill: PRIMARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-cell:selected .header-label {
|
.list-cell:selected .header-label {
|
||||||
-fx-text-fill: TEXT_FILL_PRIMARY;
|
-fx-text-fill: TEXT_FILL_HIGHLIGHTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-cell.drop-above {
|
.list-cell.drop-above {
|
||||||
@@ -379,13 +369,13 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.badge-primary {
|
.badge-primary {
|
||||||
-fx-text-fill: TEXT_FILL_WHITE;
|
-fx-text-fill: white;
|
||||||
-fx-background-color: PRIMARY_BG;
|
-fx-background-color: PRIMARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
.badge-secondary {
|
.badge-secondary {
|
||||||
-fx-text-fill: TEXT_FILL_WHITE;
|
-fx-text-fill: white;
|
||||||
-fx-background-color: SECONDARY_BG;
|
-fx-background-color: SECONDARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -395,27 +385,27 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
.password-strength-indicator .segment {
|
.password-strength-indicator .segment {
|
||||||
-fx-background-color: PASSWORD_STRENGTH_INDICATOR_BG;
|
-fx-background-color: CONTROL_BORDER_NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-strength-indicator.strength-0 .segment.active {
|
.password-strength-indicator.strength-0 .segment.active {
|
||||||
-fx-background-color: PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_0;
|
-fx-background-color: RED_5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-strength-indicator.strength-1 .segment.active {
|
.password-strength-indicator.strength-1 .segment.active {
|
||||||
-fx-background-color: PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_1;
|
-fx-background-color: ORANGE_5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-strength-indicator.strength-2 .segment.active {
|
.password-strength-indicator.strength-2 .segment.active {
|
||||||
-fx-background-color: PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_2;
|
-fx-background-color: YELLOW_5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-strength-indicator.strength-3 .segment.active {
|
.password-strength-indicator.strength-3 .segment.active {
|
||||||
-fx-background-color: PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_3;
|
-fx-background-color: PRIMARY;
|
||||||
}
|
}
|
||||||
|
|
||||||
.password-strength-indicator.strength-4 .segment.active {
|
.password-strength-indicator.strength-4 .segment.active {
|
||||||
-fx-background-color: PASSWORD_STRENGTH_INDICATOR_BG_STRENGTH_4;
|
-fx-background-color: PRIMARY_D1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@@ -442,8 +432,8 @@
|
|||||||
.text-input {
|
.text-input {
|
||||||
-fx-cursor: text;
|
-fx-cursor: text;
|
||||||
-fx-text-fill: TEXT_FILL;
|
-fx-text-fill: TEXT_FILL;
|
||||||
-fx-highlight-fill: PRIMARY_BG;
|
-fx-highlight-fill: PRIMARY;
|
||||||
-fx-prompt-text-fill: TEXT_FILL_SECONDARY;
|
-fx-prompt-text-fill: TEXT_FILL_MUTED;
|
||||||
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_NORMAL;
|
-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_NORMAL;
|
||||||
-fx-background-insets: 0, 1px;
|
-fx-background-insets: 0, 1px;
|
||||||
-fx-background-radius: 4px;
|
-fx-background-radius: 4px;
|
||||||
@@ -455,7 +445,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.text-input:disabled {
|
.text-input:disabled {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
-fx-background-color: CONTROL_BORDER_DISABLED, CONTROL_BG_DISABLED;
|
-fx-background-color: CONTROL_BORDER_DISABLED, CONTROL_BG_DISABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,7 +477,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.text-input:disabled {
|
.text-input:disabled {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
-fx-background-color: CONTROL_BORDER_DISABLED, CONTROL_BG_DISABLED;
|
-fx-background-color: CONTROL_BORDER_DISABLED, CONTROL_BG_DISABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,8 +489,8 @@
|
|||||||
-fx-padding: 0.2em 0.5em 0.2em 0.5em;
|
-fx-padding: 0.2em 0.5em 0.2em 0.5em;
|
||||||
-fx-cursor: text;
|
-fx-cursor: text;
|
||||||
-fx-text-fill: TEXT_FILL;
|
-fx-text-fill: TEXT_FILL;
|
||||||
-fx-highlight-fill: PRIMARY_BG;
|
-fx-highlight-fill: PRIMARY;
|
||||||
-fx-prompt-text-fill: TEXT_FILL_SECONDARY;
|
-fx-prompt-text-fill: TEXT_FILL_MUTED;
|
||||||
-fx-background-color: null;
|
-fx-background-color: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,7 +519,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.button:default {
|
.button:default {
|
||||||
-fx-text-fill: TEXT_FILL_WHITE;
|
-fx-text-fill: white;
|
||||||
-fx-background-color: CONTROL_PRIMARY_BORDER_NORMAL, CONTROL_PRIMARY_BG_NORMAL;
|
-fx-background-color: CONTROL_PRIMARY_BORDER_NORMAL, CONTROL_PRIMARY_BG_NORMAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,25 +528,25 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.button:default:armed {
|
.button:default:armed {
|
||||||
-fx-background-color: CONTROL_PRIMARY_BORDER_NORMAL, CONTROL_PRIMARY_BG_ARMED;
|
-fx-background-color: CONTROL_PRIMARY_BORDER_ARMED, CONTROL_PRIMARY_BG_ARMED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:disabled,
|
.button:disabled,
|
||||||
.button:default:disabled {
|
.button:default:disabled {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
-fx-background-color: CONTROL_BORDER_DISABLED, CONTROL_BG_DISABLED;
|
-fx-background-color: CONTROL_BORDER_DISABLED, CONTROL_BG_DISABLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:disabled .glyph-icon {
|
.button:disabled .glyph-icon {
|
||||||
-fx-fill: TEXT_FILL_SECONDARY;
|
-fx-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:default .glyph-icon {
|
.button:default .glyph-icon {
|
||||||
-fx-fill: TEXT_FILL_WHITE;
|
-fx-fill: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button:default .label {
|
.button:default .label {
|
||||||
-fx-text-fill: TEXT_FILL_WHITE;
|
-fx-text-fill: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-large {
|
.button-large {
|
||||||
@@ -577,7 +567,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.hyperlink.hyperlink-secondary {
|
.hyperlink.hyperlink-secondary {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hyperlink-hover-icon {
|
.hyperlink-hover-icon {
|
||||||
@@ -601,7 +591,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.check-box:disabled {
|
.check-box:disabled {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.check-box > .box {
|
.check-box > .box {
|
||||||
@@ -694,7 +684,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.choice-box:disabled > .label {
|
.choice-box:disabled > .label {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.choice-box > .open-button {
|
.choice-box > .open-button {
|
||||||
@@ -709,7 +699,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.choice-box:disabled > .open-button > .arrow {
|
.choice-box:disabled > .open-button > .arrow {
|
||||||
-fx-background-color: transparent, TEXT_FILL_SECONDARY;
|
-fx-background-color: transparent, TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.choice-box .context-menu {
|
.choice-box .context-menu {
|
||||||
@@ -758,7 +748,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.menu-item:disabled > .label {
|
.menu-item:disabled > .label {
|
||||||
-fx-text-fill: TEXT_FILL_SECONDARY;
|
-fx-text-fill: TEXT_FILL_MUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio-menu-item:checked > .left-container > .radio {
|
.radio-menu-item:checked > .left-container > .radio {
|
||||||
|
|||||||
Binary file not shown.
@@ -21,7 +21,7 @@
|
|||||||
<Region VBox.vgrow="ALWAYS"/>
|
<Region VBox.vgrow="ALWAYS"/>
|
||||||
|
|
||||||
<ImageView VBox.vgrow="ALWAYS" fitHeight="128" preserveRatio="true" smooth="true" cache="true">
|
<ImageView VBox.vgrow="ALWAYS" fitHeight="128" preserveRatio="true" smooth="true" cache="true">
|
||||||
<Image url="/bot_welcome.png"/>
|
<Image url="/bot.png"/>
|
||||||
</ImageView>
|
</ImageView>
|
||||||
|
|
||||||
<Region VBox.vgrow="ALWAYS"/>
|
<Region VBox.vgrow="ALWAYS"/>
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
<VBox alignment="CENTER" spacing="9">
|
<VBox alignment="CENTER" spacing="9">
|
||||||
<Button styleClass="button-large" text="%addvaultwizard.welcome.newButton" onAction="#createNewVault" prefWidth="Infinity">
|
<Button styleClass="button-large" text="%addvaultwizard.welcome.newButton" onAction="#createNewVault" prefWidth="Infinity">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesome5IconView glyph="SPARKLES" glyphSize="15"/>
|
<FontAwesome5IconView glyph="MAGIC" glyphSize="15"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
</Button>
|
</Button>
|
||||||
<Button styleClass="button-large" text="%addvaultwizard.welcome.existingButton" onAction="#chooseExistingVault" prefWidth="Infinity">
|
<Button styleClass="button-large" text="%addvaultwizard.welcome.existingButton" onAction="#chooseExistingVault" prefWidth="Infinity">
|
||||||
|
|||||||
@@ -4,14 +4,10 @@
|
|||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.Button?>
|
||||||
<?import javafx.scene.control.ButtonBar?>
|
<?import javafx.scene.control.ButtonBar?>
|
||||||
<?import javafx.scene.control.CheckBox?>
|
<?import javafx.scene.control.CheckBox?>
|
||||||
<?import javafx.scene.control.Label?>
|
|
||||||
<?import javafx.scene.layout.HBox?>
|
|
||||||
<?import javafx.scene.layout.Region?>
|
<?import javafx.scene.layout.Region?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
|
||||||
<?import org.cryptomator.ui.controls.FormattedLabel?>
|
<?import org.cryptomator.ui.controls.FormattedLabel?>
|
||||||
<?import org.cryptomator.ui.controls.NiceSecurePasswordField?>
|
<?import org.cryptomator.ui.controls.NiceSecurePasswordField?>
|
||||||
<?import org.cryptomator.ui.controls.PasswordStrengthIndicator?>
|
|
||||||
<VBox xmlns="http://javafx.com/javafx"
|
<VBox xmlns="http://javafx.com/javafx"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
fx:controller="org.cryptomator.ui.changepassword.ChangePasswordController"
|
fx:controller="org.cryptomator.ui.changepassword.ChangePasswordController"
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<?import javafx.scene.layout.StackPane?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
<?import javafx.scene.control.Button?>
|
||||||
<?import javafx.scene.layout.Region?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.Tooltip?>
|
<?import javafx.scene.control.Tooltip?>
|
||||||
<?import javafx.scene.layout.HBox?>
|
<?import javafx.scene.layout.HBox?>
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.scene.layout.Region?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.layout.StackPane?>
|
||||||
<?import javafx.scene.control.Button?>
|
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||||
<HBox xmlns="http://javafx.com/javafx"
|
<HBox xmlns="http://javafx.com/javafx"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
fx:id="titleBar"
|
fx:id="titleBar"
|
||||||
@@ -19,13 +19,13 @@
|
|||||||
<Insets bottom="6" left="12" right="12" top="6"/>
|
<Insets bottom="6" left="12" right="12" top="6"/>
|
||||||
</padding>
|
</padding>
|
||||||
<children>
|
<children>
|
||||||
<Label text="Cryptomator"/>
|
<Label text="CRYPTOMATOR"/>
|
||||||
<Region HBox.hgrow="ALWAYS"/>
|
<Region HBox.hgrow="ALWAYS"/>
|
||||||
<Button contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false" onAction="#showDonationKeyPreferences" focusTraversable="false" visible="${!controller.licenseHolder.validLicense}">
|
<Button contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false" onAction="#showDonationKeyPreferences" focusTraversable="false" visible="${!controller.licenseHolder.validLicense}">
|
||||||
<graphic>
|
<graphic>
|
||||||
<StackPane>
|
<StackPane>
|
||||||
<FontAwesome5IconView glyph="EXCLAMATION_CIRCLE" glyphSize="16"/>
|
<FontAwesome5IconView glyph="EXCLAMATION_CIRCLE" glyphSize="16"/>
|
||||||
<Region styleClass="update-indicator" StackPane.alignment="TOP_RIGHT" prefWidth="10" prefHeight="10" maxWidth="-Infinity" maxHeight="-Infinity"/>
|
<Region styleClass="update-indicator" StackPane.alignment="TOP_RIGHT" prefWidth="12" prefHeight="12" maxWidth="-Infinity" maxHeight="-Infinity"/>
|
||||||
</StackPane>
|
</StackPane>
|
||||||
</graphic>
|
</graphic>
|
||||||
<tooltip>
|
<tooltip>
|
||||||
@@ -36,13 +36,21 @@
|
|||||||
<graphic>
|
<graphic>
|
||||||
<StackPane>
|
<StackPane>
|
||||||
<FontAwesome5IconView glyph="COGS" glyphSize="16"/>
|
<FontAwesome5IconView glyph="COGS" glyphSize="16"/>
|
||||||
<Region styleClass="update-indicator" visible="${controller.updateAvailable}" StackPane.alignment="TOP_RIGHT" prefWidth="10" prefHeight="10" maxWidth="-Infinity" maxHeight="-Infinity"/>
|
<Region styleClass="update-indicator" visible="${controller.updateAvailable}" StackPane.alignment="TOP_RIGHT" prefWidth="12" prefHeight="12" maxWidth="-Infinity" maxHeight="-Infinity"/>
|
||||||
</StackPane>
|
</StackPane>
|
||||||
</graphic>
|
</graphic>
|
||||||
<tooltip>
|
<tooltip>
|
||||||
<Tooltip text="%main.preferencesBtn.tooltip"/>
|
<Tooltip text="%main.preferencesBtn.tooltip"/>
|
||||||
</tooltip>
|
</tooltip>
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false" onAction="#minimize" focusTraversable="false" visible="${!controller.minimizeToSysTray}" managed="${!controller.minimizeToSysTray}">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="WINDOW_MINIMIZE" glyphSize="12"/>
|
||||||
|
</graphic>
|
||||||
|
<tooltip>
|
||||||
|
<Tooltip text="%main.minimizeBtn.tooltip"/>
|
||||||
|
</tooltip>
|
||||||
|
</Button>
|
||||||
<Button contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false" onAction="#close" focusTraversable="false">
|
<Button contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false" onAction="#close" focusTraversable="false">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesome5IconView glyph="TIMES" glyphSize="16"/>
|
<FontAwesome5IconView glyph="TIMES" glyphSize="16"/>
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ButtonBar?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.layout.Region?>
|
||||||
|
<?import javafx.scene.layout.StackPane?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.shape.Circle?>
|
||||||
|
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||||
|
<VBox xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="org.cryptomator.ui.migration.MigrationCapabilityErrorController"
|
||||||
|
minWidth="400"
|
||||||
|
maxWidth="400"
|
||||||
|
minHeight="145"
|
||||||
|
spacing="12">
|
||||||
|
<padding>
|
||||||
|
<Insets topRightBottomLeft="12"/>
|
||||||
|
</padding>
|
||||||
|
<children>
|
||||||
|
<HBox spacing="12" VBox.vgrow="ALWAYS">
|
||||||
|
<StackPane alignment="CENTER" HBox.hgrow="NEVER">
|
||||||
|
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
||||||
|
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="EXCLAMATION" glyphSize="24"/>
|
||||||
|
</StackPane>
|
||||||
|
<VBox spacing="6" HBox.hgrow="ALWAYS">
|
||||||
|
<Label text="%migration.error.missingFileSystemCapabilities.title" wrapText="true"/>
|
||||||
|
<Label text="%migration.error.missingFileSystemCapabilities.description" wrapText="true"/>
|
||||||
|
<Label text="${controller.missingCapabilityDescription}" wrapText="true"/>
|
||||||
|
</VBox>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
<Region VBox.vgrow="ALWAYS"/>
|
||||||
|
|
||||||
|
<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
|
||||||
|
<ButtonBar buttonMinWidth="120" buttonOrder="B+U">
|
||||||
|
<buttons>
|
||||||
|
<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" cancelButton="true" onAction="#back"/>
|
||||||
|
<Region ButtonBar.buttonData="OTHER"/>
|
||||||
|
</buttons>
|
||||||
|
</ButtonBar>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ButtonBar?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import javafx.scene.layout.Region?>
|
||||||
|
<VBox xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="org.cryptomator.ui.migration.MigrationGenericErrorController"
|
||||||
|
minWidth="400"
|
||||||
|
maxWidth="400"
|
||||||
|
minHeight="145"
|
||||||
|
spacing="12">
|
||||||
|
<padding>
|
||||||
|
<Insets topRightBottomLeft="12"/>
|
||||||
|
</padding>
|
||||||
|
<children>
|
||||||
|
<fx:include source="/fxml/stacktrace.fxml"/>
|
||||||
|
|
||||||
|
<Region VBox.vgrow="ALWAYS"/>
|
||||||
|
|
||||||
|
<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
|
||||||
|
<ButtonBar buttonMinWidth="120" buttonOrder="B+U">
|
||||||
|
<buttons>
|
||||||
|
<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" cancelButton="true" onAction="#back"/>
|
||||||
|
<Region ButtonBar.buttonData="OTHER"/>
|
||||||
|
</buttons>
|
||||||
|
</ButtonBar>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.licenseHolder.validLicense}">
|
<HBox spacing="12" alignment="CENTER_LEFT" visible="${controller.licenseHolder.validLicense}">
|
||||||
<StackPane alignment="CENTER" HBox.hgrow="NEVER">
|
<StackPane alignment="CENTER" HBox.hgrow="NEVER">
|
||||||
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
<Circle styleClass="glyph-icon-primary" radius="24"/>
|
||||||
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="USER_CROWN" glyphSize="24"/>
|
<FontAwesome5IconView styleClass="glyph-icon-white" glyph="CROWN" glyphSize="24"/>
|
||||||
</StackPane>
|
</StackPane>
|
||||||
<FormattedLabel format="%preferences.donationKey.registeredFor" arg1="${controller.licenseHolder.licenseSubject}" wrapText="true"/>
|
<FormattedLabel format="%preferences.donationKey.registeredFor" arg1="${controller.licenseHolder.licenseSubject}" wrapText="true"/>
|
||||||
</HBox>
|
</HBox>
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
<FontAwesome5IconView glyph="PRINT"/>
|
<FontAwesome5IconView glyph="PRINT"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
</Button>
|
</Button>
|
||||||
<Button text="%generic.button.copy" ButtonBar.buttonData="RIGHT" onAction="#copyRecoveryKey">
|
<Button fx:id="copyButton" text="%generic.button.copy" ButtonBar.buttonData="RIGHT" onAction="#copyRecoveryKey">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesome5IconView glyph="COPY"/>
|
<FontAwesome5IconView glyph="COPY"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ButtonBar?>
|
||||||
|
<?import javafx.scene.control.TextArea?>
|
||||||
|
<?import javafx.scene.layout.Region?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import org.cryptomator.ui.controls.FormattedLabel?>
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||||
|
<VBox xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="org.cryptomator.ui.recoverykey.RecoveryKeyRecoverController"
|
||||||
|
minWidth="400"
|
||||||
|
maxWidth="400"
|
||||||
|
minHeight="145"
|
||||||
|
spacing="12"
|
||||||
|
alignment="TOP_CENTER">
|
||||||
|
<padding>
|
||||||
|
<Insets topRightBottomLeft="12"/>
|
||||||
|
</padding>
|
||||||
|
<children>
|
||||||
|
<FormattedLabel format="%recoveryKey.recover.prompt" arg1="${controller.vault.displayableName}" wrapText="true"/>
|
||||||
|
|
||||||
|
<TextArea wrapText="true" prefRowCount="4" fx:id="textarea" textFormatter="${controller.recoveryKeyTextFormatter}" onKeyPressed="#onKeyPressed"/>
|
||||||
|
|
||||||
|
<Label text="%recoveryKey.recover.validKey" graphicTextGap="6" contentDisplay="LEFT" visible="${controller.validRecoveryKey}">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="CHECK"/>
|
||||||
|
</graphic>
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<Region VBox.vgrow="ALWAYS"/>
|
||||||
|
|
||||||
|
<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
|
||||||
|
<ButtonBar buttonMinWidth="120" buttonOrder="C+X">
|
||||||
|
<buttons>
|
||||||
|
<Button text="%generic.button.cancel" ButtonBar.buttonData="CANCEL_CLOSE" cancelButton="true" onAction="#close" />
|
||||||
|
<Button text="%generic.button.next" ButtonBar.buttonData="NEXT_FORWARD" defaultButton="true" onAction="#recover" disable="${!controller.validRecoveryKey}" />
|
||||||
|
</buttons>
|
||||||
|
</ButtonBar>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.control.ButtonBar?>
|
||||||
|
<?import javafx.scene.layout.Region?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<VBox xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="org.cryptomator.ui.recoverykey.RecoveryKeyResetPasswordController"
|
||||||
|
minWidth="400"
|
||||||
|
maxWidth="400"
|
||||||
|
minHeight="145"
|
||||||
|
spacing="12"
|
||||||
|
alignment="TOP_CENTER">
|
||||||
|
<padding>
|
||||||
|
<Insets topRightBottomLeft="12"/>
|
||||||
|
</padding>
|
||||||
|
<children>
|
||||||
|
<fx:include source="/fxml/new_password.fxml"/>
|
||||||
|
|
||||||
|
<Region VBox.vgrow="ALWAYS"/>
|
||||||
|
|
||||||
|
<VBox alignment="BOTTOM_CENTER" VBox.vgrow="ALWAYS">
|
||||||
|
<ButtonBar buttonMinWidth="120" buttonOrder="B+I">
|
||||||
|
<buttons>
|
||||||
|
<Button text="%generic.button.back" ButtonBar.buttonData="BACK_PREVIOUS" cancelButton="true" onAction="#back" />
|
||||||
|
<Button text="%generic.button.done" ButtonBar.buttonData="FINISH" defaultButton="true" onAction="#done" disable="${controller.invalidNewPassword}"/>
|
||||||
|
</buttons>
|
||||||
|
</ButtonBar>
|
||||||
|
</VBox>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
</HBox>
|
</HBox>
|
||||||
<Hyperlink styleClass="hyperlink-secondary,hyperlink-hover-icon" text="${controller.vault.displayablePath}" textOverrun="CENTER_ELLIPSIS" onAction="#revealStorageLocation">
|
<Hyperlink styleClass="hyperlink-secondary,hyperlink-hover-icon" text="${controller.vault.displayablePath}" textOverrun="CENTER_ELLIPSIS" onAction="#revealStorageLocation">
|
||||||
<graphic>
|
<graphic>
|
||||||
<FontAwesome5IconView styleClass="glyph-icon-secondary" glyph="EYE"/>
|
<FontAwesome5IconView styleClass="glyph-icon-muted" glyph="EYE"/>
|
||||||
</graphic>
|
</graphic>
|
||||||
<tooltip>
|
<tooltip>
|
||||||
<Tooltip text="${controller.vault.displayablePath}"/>
|
<Tooltip text="${controller.vault.displayablePath}"/>
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.control.Label?>
|
||||||
<?import javafx.scene.control.ListView?>
|
<?import javafx.scene.control.ListView?>
|
||||||
<?import javafx.scene.control.MenuItem?>
|
<?import javafx.scene.control.MenuItem?>
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
<?import javafx.scene.layout.Region?>
|
||||||
<?import javafx.scene.layout.HBox?>
|
|
||||||
<?import javafx.scene.layout.StackPane?>
|
<?import javafx.scene.layout.StackPane?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<?import javafx.scene.shape.Arc?>
|
<?import javafx.scene.shape.Arc?>
|
||||||
@@ -25,12 +24,11 @@
|
|||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</contextMenu>
|
</contextMenu>
|
||||||
</ListView>
|
</ListView>
|
||||||
<AnchorPane visible="${controller.emptyVaultList}">
|
<VBox visible="${controller.emptyVaultList}" spacing="6" alignment="CENTER">
|
||||||
<HBox AnchorPane.leftAnchor="10" AnchorPane.rightAnchor="10" AnchorPane.bottomAnchor="100" alignment="BOTTOM_LEFT">
|
<Region VBox.vgrow="ALWAYS"/>
|
||||||
<Label textAlignment="CENTER" text="%main.vaultlist.emptyList.onboardingInstruction" wrapText="true"/>
|
<Label VBox.vgrow="NEVER" text="%main.vaultlist.emptyList.onboardingInstruction" wrapText="true"/>
|
||||||
</HBox>
|
<Arc VBox.vgrow="NEVER" styleClass="onboarding-overlay-arc" type="OPEN" centerX="50" centerY="0" radiusY="100" radiusX="50" startAngle="0" length="-60" strokeWidth="1"/>
|
||||||
<Arc styleClass="onboarding-overlay-arc" AnchorPane.bottomAnchor="5" type="OPEN" centerX="-10" centerY="0" radiusY="100" radiusX="60" startAngle="0" length="-60" strokeWidth="1"/>
|
</VBox>
|
||||||
</AnchorPane>
|
|
||||||
</StackPane>
|
</StackPane>
|
||||||
<Button styleClass="toolbar-button" text="%main.vaultlist.addVaultBtn" onAction="#didClickAddVault" alignment="BASELINE_CENTER" maxWidth="Infinity">
|
<Button styleClass="toolbar-button" text="%main.vaultlist.addVaultBtn" onAction="#didClickAddVault" alignment="BASELINE_CENTER" maxWidth="Infinity">
|
||||||
<graphic>
|
<graphic>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
fx:id="tabPane"
|
fx:id="tabPane"
|
||||||
fx:controller="org.cryptomator.ui.vaultoptions.VaultOptionsController"
|
fx:controller="org.cryptomator.ui.vaultoptions.VaultOptionsController"
|
||||||
minWidth="400"
|
prefWidth="400"
|
||||||
tabMinWidth="60"
|
tabMinWidth="60"
|
||||||
tabClosingPolicy="UNAVAILABLE"
|
tabClosingPolicy="UNAVAILABLE"
|
||||||
tabDragPolicy="FIXED">
|
tabDragPolicy="FIXED">
|
||||||
@@ -28,5 +28,13 @@
|
|||||||
<fx:include source="/fxml/vault_options_mount.fxml"/>
|
<fx:include source="/fxml/vault_options_mount.fxml"/>
|
||||||
</content>
|
</content>
|
||||||
</Tab>
|
</Tab>
|
||||||
|
<Tab fx:id="keyTab" text="%vaultOptions.masterkey">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="KEY"/>
|
||||||
|
</graphic>
|
||||||
|
<content>
|
||||||
|
<fx:include source="/fxml/vault_options_masterkey.fxml"/>
|
||||||
|
</content>
|
||||||
|
</Tab>
|
||||||
</tabs>
|
</tabs>
|
||||||
</TabPane>
|
</TabPane>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.Insets?>
|
||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.CheckBox?>
|
||||||
<?import javafx.scene.layout.VBox?>
|
<?import javafx.scene.layout.VBox?>
|
||||||
<VBox xmlns="http://javafx.com/javafx"
|
<VBox xmlns="http://javafx.com/javafx"
|
||||||
xmlns:fx="http://javafx.com/fxml"
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
@@ -11,7 +11,6 @@
|
|||||||
<Insets topRightBottomLeft="12"/>
|
<Insets topRightBottomLeft="12"/>
|
||||||
</padding>
|
</padding>
|
||||||
<children>
|
<children>
|
||||||
<Button text="%vaultOptions.general.changePasswordBtn" onAction="#changePassword"/>
|
<CheckBox text="%vaultOptions.general.unlockAfterStartup" fx:id="unlockOnStartupCheckbox"/>
|
||||||
<Button text="%vaultOptions.general.showRecoveryKeyBtn" onAction="#showRecoveryKey"/>
|
|
||||||
</children>
|
</children>
|
||||||
</VBox>
|
</VBox>
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.Button?>
|
||||||
|
<?import javafx.scene.layout.VBox?>
|
||||||
|
<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.layout.Region?>
|
||||||
|
<VBox xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="org.cryptomator.ui.vaultoptions.MasterkeyOptionsController"
|
||||||
|
spacing="6"
|
||||||
|
alignment="TOP_CENTER">
|
||||||
|
<padding>
|
||||||
|
<Insets topRightBottomLeft="12"/>
|
||||||
|
</padding>
|
||||||
|
<children>
|
||||||
|
<Button text="%vaultOptions.masterkey.changePasswordBtn" onAction="#changePassword" contentDisplay="LEFT">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="KEY"/>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Region VBox.vgrow="ALWAYS"/>
|
||||||
|
|
||||||
|
<Label maxWidth="-Infinity" text="%vaultOptions.masterkey.recoveryKeyExpanation" wrapText="true"/>
|
||||||
|
<HBox spacing="6" alignment="CENTER">
|
||||||
|
<Button text="%vaultOptions.masterkey.showRecoveryKeyBtn" onAction="#showRecoveryKey" contentDisplay="LEFT">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="EYE"/>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
<Button text="%vaultOptions.masterkey.recoverPasswordBtn" onAction="#showRecoverVaultDialogue" contentDisplay="LEFT">
|
||||||
|
<graphic>
|
||||||
|
<FontAwesome5IconView glyph="SYNC"/>
|
||||||
|
</graphic>
|
||||||
|
</Button>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
<Region VBox.vgrow="ALWAYS"/>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
@@ -8,6 +8,7 @@ generic.button.back=Back
|
|||||||
generic.button.cancel=Cancel
|
generic.button.cancel=Cancel
|
||||||
generic.button.change=Change
|
generic.button.change=Change
|
||||||
generic.button.copy=Copy
|
generic.button.copy=Copy
|
||||||
|
generic.button.copied=Copied!
|
||||||
generic.button.done=Done
|
generic.button.done=Done
|
||||||
generic.button.next=Next
|
generic.button.next=Next
|
||||||
generic.button.print=Print
|
generic.button.print=Print
|
||||||
@@ -49,12 +50,12 @@ addvaultwizard.new.generateRecoveryKeyChoice=You won't be able to access your da
|
|||||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Yes please, better safe than sorry
|
addvaultwizard.new.generateRecoveryKeyChoice.yes=Yes please, better safe than sorry
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice.no=No thanks, I will not lose my password
|
addvaultwizard.new.generateRecoveryKeyChoice.no=No thanks, I will not lose my password
|
||||||
### Information
|
### Information
|
||||||
addvault.new.readme.storageLocation.fileName=WHAT IS THIS DIRECTORY.rtf
|
addvault.new.readme.storageLocation.fileName=HELP.rtf
|
||||||
addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ VAULT FILES ⚠️
|
addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ VAULT DIRECTORY ⚠️
|
||||||
addvault.new.readme.storageLocation.2=This is your vault's storage location. {\\b DO NOT} alter any files within this directory.
|
addvault.new.readme.storageLocation.2=This is your vault's storage location. {\\b DO NOT} alter any files within this directory.
|
||||||
addvault.new.readme.storageLocation.3=If you want to encrypt files using Cryptomator, unlock the vault and use the provided drive.
|
addvault.new.readme.storageLocation.3=If you want to encrypt files using Cryptomator, unlock the vault and use the provided drive.
|
||||||
addvault.new.readme.storageLocation.4=If you need help, try %s.
|
addvault.new.readme.storageLocation.4=If you need help, try %s.
|
||||||
addvault.new.readme.accessLocation.fileName=WELCOME TO YOUR VAULT.rtf
|
addvault.new.readme.accessLocation.fileName=WELCOME.rtf
|
||||||
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ ENCRYPTED VOLUME 🔐️
|
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ ENCRYPTED VOLUME 🔐️
|
||||||
addvault.new.readme.accessLocation.2=This is your vault's access location. Any files added to this volume will be encrypted by Cryptomator. To access this volume at a later point in time, simply unlock it again from within the Cryptomator application.
|
addvault.new.readme.accessLocation.2=This is your vault's access location. Any files added to this volume will be encrypted by Cryptomator. To access this volume at a later point in time, simply unlock it again from within the Cryptomator application.
|
||||||
addvault.new.readme.accessLocation.3=Feel free to remove this file.
|
addvault.new.readme.accessLocation.3=Feel free to remove this file.
|
||||||
@@ -104,6 +105,11 @@ migration.run.startMigrationBtn=Migrate Vault
|
|||||||
## Sucess
|
## Sucess
|
||||||
migration.success.nextStepsInstructions=Migrated "%s" successfully.\nYou can now unlock your vault.
|
migration.success.nextStepsInstructions=Migrated "%s" successfully.\nYou can now unlock your vault.
|
||||||
migration.success.unlockNow=Unlock Now
|
migration.success.unlockNow=Unlock Now
|
||||||
|
## Missing file system capabilities
|
||||||
|
migration.error.missingFileSystemCapabilities.title=Unsupported File System
|
||||||
|
migration.error.missingFileSystemCapabilities.description=Migration was not started, because your vault is located on an inadequate file system.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=The file system does not support long file names.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=The file system does not support long paths.
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
preferences.title=Preferences
|
preferences.title=Preferences
|
||||||
@@ -135,6 +141,7 @@ preferences.donationKey.getDonationKey=Get a donation key
|
|||||||
|
|
||||||
# Main Window
|
# Main Window
|
||||||
main.closeBtn.tooltip=Close
|
main.closeBtn.tooltip=Close
|
||||||
|
main.minimizeBtn.tooltip=Minimize
|
||||||
main.preferencesBtn.tooltip=Preferences
|
main.preferencesBtn.tooltip=Preferences
|
||||||
main.donationKeyMissing.tooltip=Please consider donating
|
main.donationKeyMissing.tooltip=Please consider donating
|
||||||
## Drag 'n' Drop
|
## Drag 'n' Drop
|
||||||
@@ -171,8 +178,7 @@ wrongFileAlert.information=You have tried to add a file or folder that does not
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
vaultOptions.general=General
|
vaultOptions.general=General
|
||||||
vaultOptions.general.changePasswordBtn=Change Password
|
vaultOptions.general.unlockAfterStartup=Unlock vault when starting Cryptomator
|
||||||
vaultOptions.general.showRecoveryKeyBtn=Display Recovery Key
|
|
||||||
## Mount
|
## Mount
|
||||||
vaultOptions.mount=Mounting
|
vaultOptions.mount=Mounting
|
||||||
vaultOptions.mount.readonly=Read-Only
|
vaultOptions.mount.readonly=Read-Only
|
||||||
@@ -185,12 +191,20 @@ vaultOptions.mount.mountPoint.driveLetter=Use assigned drive letter
|
|||||||
vaultOptions.mount.mountPoint.custom=Custom path
|
vaultOptions.mount.mountPoint.custom=Custom path
|
||||||
vaultOptions.mount.mountPoint.directoryPickerButton=Choose…
|
vaultOptions.mount.mountPoint.directoryPickerButton=Choose…
|
||||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Pick an empty directory
|
vaultOptions.mount.mountPoint.directoryPickerTitle=Pick an empty directory
|
||||||
|
## Master Key
|
||||||
|
vaultOptions.masterkey=Password
|
||||||
|
vaultOptions.masterkey.changePasswordBtn=Change Password
|
||||||
|
vaultOptions.masterkey.recoveryKeyExpanation=A recovery key is your only means to restore access to a vault if you lose your password.
|
||||||
|
vaultOptions.masterkey.showRecoveryKeyBtn=Display Recovery Key
|
||||||
|
vaultOptions.masterkey.recoverPasswordBtn=Recover Password
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
recoveryKey.title=Recovery Key
|
recoveryKey.title=Recovery Key
|
||||||
recoveryKey.enterPassword.prompt=Enter your password to show the recovery key for "%s":
|
recoveryKey.enterPassword.prompt=Enter your password to show the recovery key for "%s":
|
||||||
recoveryKey.display.message=The following recovery key can be used to restore access to "%s":
|
recoveryKey.display.message=The following recovery key can be used to restore access to "%s":
|
||||||
recoveryKey.display.StorageHints=Keep it somewhere very secure, e.g.:\n • Store it using a password manager\n • Save it on a USB flash drive\n • Print it on paper
|
recoveryKey.display.StorageHints=Keep it somewhere very secure, e.g.:\n • Store it using a password manager\n • Save it on a USB flash drive\n • Print it on paper
|
||||||
|
recoveryKey.recover.prompt=Enter your recovery key for "%s":
|
||||||
|
recoveryKey.recover.validKey=This is a valid recovery key
|
||||||
|
|
||||||
# New Password
|
# New Password
|
||||||
newPassword.promptText=Enter a new password
|
newPassword.promptText=Enter a new password
|
||||||
|
|||||||
@@ -43,8 +43,10 @@ addvault.new.readme.storageLocation.4=لو انك تحتاج مساعدة, جر
|
|||||||
## Existing
|
## Existing
|
||||||
addvaultwizard.existing.chooseBtn=اختر…
|
addvaultwizard.existing.chooseBtn=اختر…
|
||||||
## Success
|
## Success
|
||||||
|
addvaultwizard.success.unlockNow=افتح الان
|
||||||
|
|
||||||
# Remove Vault
|
# Remove Vault
|
||||||
|
removeVault.title=احذف الحافظة
|
||||||
removeVault.confirmBtn=احذف الحافظة
|
removeVault.confirmBtn=احذف الحافظة
|
||||||
|
|
||||||
# Change Password
|
# Change Password
|
||||||
@@ -69,6 +71,7 @@ migration.start.confirm=نعم, محفظتي متزامنة بالكامل
|
|||||||
migration.run.startMigrationBtn=ترقية الحافظة
|
migration.run.startMigrationBtn=ترقية الحافظة
|
||||||
## Sucess
|
## Sucess
|
||||||
migration.success.unlockNow=افتح الان
|
migration.success.unlockNow=افتح الان
|
||||||
|
## Missing file system capabilities
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
preferences.title=تفضيلات
|
preferences.title=تفضيلات
|
||||||
@@ -86,6 +89,7 @@ preferences.updates.checkNowBtn=تحقق الان
|
|||||||
main.preferencesBtn.tooltip=تفضيلات
|
main.preferencesBtn.tooltip=تفضيلات
|
||||||
## Drag 'n' Drop
|
## Drag 'n' Drop
|
||||||
## Vault List
|
## Vault List
|
||||||
|
main.vaultlist.contextMenu.remove=احذف الحافظة
|
||||||
main.vaultlist.addVaultBtn=أضِف مخزنًا
|
main.vaultlist.addVaultBtn=أضِف مخزنًا
|
||||||
## Vault Detail
|
## Vault Detail
|
||||||
### Locked
|
### Locked
|
||||||
@@ -104,9 +108,13 @@ main.vaultDetail.migrateButton=ترقية الحافظة
|
|||||||
vaultOptions.general=عام
|
vaultOptions.general=عام
|
||||||
## Mount
|
## Mount
|
||||||
vaultOptions.mount.mountPoint.directoryPickerButton=اختر…
|
vaultOptions.mount.mountPoint.directoryPickerButton=اختر…
|
||||||
|
## Master Key
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
|
|
||||||
# New Password
|
# New Password
|
||||||
|
newPassword.reenterPassword=تأكيد كلمة المرور الجديدة
|
||||||
|
newPassword.passwordsMatch=كلمات المرور متطابقة!
|
||||||
|
newPassword.passwordsDoNotMatch=كلمات المرور غير متطابقة
|
||||||
|
|
||||||
# Quit
|
# Quit
|
||||||
|
|||||||
@@ -43,12 +43,9 @@ addvaultwizard.new.createVaultBtn=Vytvořit trezor
|
|||||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Ano prosím, jistota je jistota
|
addvaultwizard.new.generateRecoveryKeyChoice.yes=Ano prosím, jistota je jistota
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice.no=Ne, díky, neztratím své heslo
|
addvaultwizard.new.generateRecoveryKeyChoice.no=Ne, díky, neztratím své heslo
|
||||||
### Information
|
### Information
|
||||||
addvault.new.readme.storageLocation.fileName=CO JE TOTO ZA ADRESÁŘ.rtf
|
|
||||||
addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ OBSAH TREZORU ⚠️
|
|
||||||
addvault.new.readme.storageLocation.2=Toto je umístění vašeho trezoru. NEMĚŇTE žádné soubory uvnitř adresáře.
|
addvault.new.readme.storageLocation.2=Toto je umístění vašeho trezoru. NEMĚŇTE žádné soubory uvnitř adresáře.
|
||||||
addvault.new.readme.storageLocation.3=Pokud chcete zašifrovat soubory pomocí Cryptomatoru, odemkněte trezor a použijte přidělený disk.
|
addvault.new.readme.storageLocation.3=Pokud chcete zašifrovat soubory pomocí Cryptomatoru, odemkněte trezor a použijte přidělený disk.
|
||||||
addvault.new.readme.storageLocation.4=Pokud potřebujete pomoc, zkuste %s.
|
addvault.new.readme.storageLocation.4=Pokud potřebujete pomoc, zkuste %s.
|
||||||
addvault.new.readme.accessLocation.fileName=Vítej ve svém novém trezoru.rtf
|
|
||||||
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ ZAŠIFROVANÁ JEDNOTKA 🔐️
|
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ ZAŠIFROVANÁ JEDNOTKA 🔐️
|
||||||
addvault.new.readme.accessLocation.2=Toto je přístup k vašemu trezoru. Všechny soubory přidané do této jednotky budou šifrovány pomocí Cryptomatoru. Pro přístup k této jednotce kdykoliv v budoucnu ji jednoduše opět odemkněte pomocí Cryptomatoru.
|
addvault.new.readme.accessLocation.2=Toto je přístup k vašemu trezoru. Všechny soubory přidané do této jednotky budou šifrovány pomocí Cryptomatoru. Pro přístup k této jednotce kdykoliv v budoucnu ji jednoduše opět odemkněte pomocí Cryptomatoru.
|
||||||
addvault.new.readme.accessLocation.3=Tento soubor můžete odstranit.
|
addvault.new.readme.accessLocation.3=Tento soubor můžete odstranit.
|
||||||
@@ -96,6 +93,7 @@ migration.run.startMigrationBtn=Migrovat trezor
|
|||||||
## Sucess
|
## Sucess
|
||||||
migration.success.nextStepsInstructions=Migrace "%s" byla úspěšná.\nNyní můžete svůj trezor odemknout.
|
migration.success.nextStepsInstructions=Migrace "%s" byla úspěšná.\nNyní můžete svůj trezor odemknout.
|
||||||
migration.success.unlockNow=Odemknout nyní
|
migration.success.unlockNow=Odemknout nyní
|
||||||
|
## Missing file system capabilities
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
preferences.title=Nastavení
|
preferences.title=Nastavení
|
||||||
@@ -158,8 +156,6 @@ wrongFileAlert.information=Zkoušíte přidat soubor či adresář, který zřej
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
vaultOptions.general=Obecné
|
vaultOptions.general=Obecné
|
||||||
vaultOptions.general.changePasswordBtn=Změnit heslo
|
|
||||||
vaultOptions.general.showRecoveryKeyBtn=Zobrazit klíč k obnově
|
|
||||||
## Mount
|
## Mount
|
||||||
vaultOptions.mount=Připojení
|
vaultOptions.mount=Připojení
|
||||||
vaultOptions.mount.readonly=Pouze pro čtení
|
vaultOptions.mount.readonly=Pouze pro čtení
|
||||||
@@ -172,6 +168,9 @@ vaultOptions.mount.mountPoint.driveLetter=Použít přiřazené písmeno
|
|||||||
vaultOptions.mount.mountPoint.custom=Vlastní cesta
|
vaultOptions.mount.mountPoint.custom=Vlastní cesta
|
||||||
vaultOptions.mount.mountPoint.directoryPickerButton=Vybrat...
|
vaultOptions.mount.mountPoint.directoryPickerButton=Vybrat...
|
||||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Vyberte prázdný adresář
|
vaultOptions.mount.mountPoint.directoryPickerTitle=Vyberte prázdný adresář
|
||||||
|
## Master Key
|
||||||
|
vaultOptions.masterkey.changePasswordBtn=Změnit heslo
|
||||||
|
vaultOptions.masterkey.showRecoveryKeyBtn=Zobrazit klíč k obnově
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
recoveryKey.title=Klíč k obnově
|
recoveryKey.title=Klíč k obnově
|
||||||
@@ -180,6 +179,9 @@ recoveryKey.display.message=Následující obnovovací klíč může být použi
|
|||||||
recoveryKey.display.StorageHints=Uchovejte ho někde velmi bezpečně, např.\n • Uložit pomocí správce hesel\n • Uložit na USB flash disk\n • Vytisknout na papír
|
recoveryKey.display.StorageHints=Uchovejte ho někde velmi bezpečně, např.\n • Uložit pomocí správce hesel\n • Uložit na USB flash disk\n • Vytisknout na papír
|
||||||
|
|
||||||
# New Password
|
# New Password
|
||||||
|
newPassword.reenterPassword=Potvrď nové heslo
|
||||||
|
newPassword.passwordsMatch=Hesla se shodují!
|
||||||
|
newPassword.passwordsDoNotMatch=Hesla se neshodují
|
||||||
passwordStrength.messageLabel.0=velmi slabé
|
passwordStrength.messageLabel.0=velmi slabé
|
||||||
passwordStrength.messageLabel.1=slabé
|
passwordStrength.messageLabel.1=slabé
|
||||||
passwordStrength.messageLabel.2=dostatečné
|
passwordStrength.messageLabel.2=dostatečné
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ generic.button.back=Zurück
|
|||||||
generic.button.cancel=Abbrechen
|
generic.button.cancel=Abbrechen
|
||||||
generic.button.change=Ändern
|
generic.button.change=Ändern
|
||||||
generic.button.copy=Kopieren
|
generic.button.copy=Kopieren
|
||||||
|
generic.button.copied=Kopiert!
|
||||||
generic.button.done=Fertig
|
generic.button.done=Fertig
|
||||||
generic.button.next=Weiter
|
generic.button.next=Weiter
|
||||||
generic.button.print=Drucken
|
generic.button.print=Drucken
|
||||||
@@ -48,12 +49,9 @@ addvaultwizard.new.generateRecoveryKeyChoice=Ohne dieses Passwort kannst du auf
|
|||||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Ja bitte, sicher ist sicher
|
addvaultwizard.new.generateRecoveryKeyChoice.yes=Ja bitte, sicher ist sicher
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice.no=Nein danke, ich werde mein Passwort nicht verlieren
|
addvaultwizard.new.generateRecoveryKeyChoice.no=Nein danke, ich werde mein Passwort nicht verlieren
|
||||||
### Information
|
### Information
|
||||||
addvault.new.readme.storageLocation.fileName=WAS BEDEUTET DIESER ORDNER.rtf
|
|
||||||
addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ TRESORDATEIEN ⚠️
|
|
||||||
addvault.new.readme.storageLocation.2=Dies ist der Speicherort deines Tresors. Ändere {\\b KEINE} Dateien in diesem Verzeichnis.
|
addvault.new.readme.storageLocation.2=Dies ist der Speicherort deines Tresors. Ändere {\\b KEINE} Dateien in diesem Verzeichnis.
|
||||||
addvault.new.readme.storageLocation.3=Wenn du Dateien mit Cryptomator verschlüsseln möchtest, entsperre den Tresor und verwende das zur Verfügung gestellte Laufwerk.
|
addvault.new.readme.storageLocation.3=Wenn du Dateien mit Cryptomator verschlüsseln möchtest, entsperre den Tresor und verwende das zur Verfügung gestellte Laufwerk.
|
||||||
addvault.new.readme.storageLocation.4=Falls du Hilfe brauchst, versuche %s.
|
addvault.new.readme.storageLocation.4=Falls du Hilfe brauchst, versuche %s.
|
||||||
addvault.new.readme.accessLocation.fileName=WILLKOMMEN ZU DEINEM TRESOR.rtf
|
|
||||||
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ VERSCHLÜSSELTES LAUFWERK 🔐️
|
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ VERSCHLÜSSELTES LAUFWERK 🔐️
|
||||||
addvault.new.readme.accessLocation.2=Dies ist dein Tresorlaufwerk. Alle zu diesem Laufwerk hinzugefügten Dateien werden von Cryptomator verschlüsselt. Um zu einem späteren Zeitpunkt auf dieses Laufwerk zuzugreifen, entsperre es einfach wieder aus der Cryptomator-Anwendung heraus.
|
addvault.new.readme.accessLocation.2=Dies ist dein Tresorlaufwerk. Alle zu diesem Laufwerk hinzugefügten Dateien werden von Cryptomator verschlüsselt. Um zu einem späteren Zeitpunkt auf dieses Laufwerk zuzugreifen, entsperre es einfach wieder aus der Cryptomator-Anwendung heraus.
|
||||||
addvault.new.readme.accessLocation.3=Diese Datei kannst du löschen.
|
addvault.new.readme.accessLocation.3=Diese Datei kannst du löschen.
|
||||||
@@ -103,6 +101,11 @@ migration.run.startMigrationBtn=Tresor migrieren
|
|||||||
## Sucess
|
## Sucess
|
||||||
migration.success.nextStepsInstructions=„%s“ erfolgreich migriert.\nDu kannst deinen Tresor jetzt entsperren.
|
migration.success.nextStepsInstructions=„%s“ erfolgreich migriert.\nDu kannst deinen Tresor jetzt entsperren.
|
||||||
migration.success.unlockNow=Jetzt entsperren
|
migration.success.unlockNow=Jetzt entsperren
|
||||||
|
## Missing file system capabilities
|
||||||
|
migration.error.missingFileSystemCapabilities.title=Nicht unterstütztes Dateisystem
|
||||||
|
migration.error.missingFileSystemCapabilities.description=Die Migration wurde nicht gestartet, da sich dein Tresor auf einem ungeeigneten Dateisystem befindet.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=Das Dateisystem unterstützt keine langen Dateinamen.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=Das Dateisystem unterstützt keine langen Pfadnamen.
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
preferences.title=Einstellungen
|
preferences.title=Einstellungen
|
||||||
@@ -134,6 +137,7 @@ preferences.donationKey.getDonationKey=Einen Spendenschlüssel erhalten
|
|||||||
|
|
||||||
# Main Window
|
# Main Window
|
||||||
main.closeBtn.tooltip=Schließen
|
main.closeBtn.tooltip=Schließen
|
||||||
|
main.minimizeBtn.tooltip=Minimieren
|
||||||
main.preferencesBtn.tooltip=Einstellungen
|
main.preferencesBtn.tooltip=Einstellungen
|
||||||
main.donationKeyMissing.tooltip=Bitte denke über eine Spende nach
|
main.donationKeyMissing.tooltip=Bitte denke über eine Spende nach
|
||||||
## Drag 'n' Drop
|
## Drag 'n' Drop
|
||||||
@@ -170,8 +174,7 @@ wrongFileAlert.information=Du hast versucht, eine Datei oder einen Ordner hinzuz
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
vaultOptions.general=Allgemein
|
vaultOptions.general=Allgemein
|
||||||
vaultOptions.general.changePasswordBtn=Passwort ändern
|
vaultOptions.general.unlockAfterStartup=Tresor beim Starten von Cryptomator entsperren
|
||||||
vaultOptions.general.showRecoveryKeyBtn=Wiederherstellungsschlüssel anzeigen
|
|
||||||
## Mount
|
## Mount
|
||||||
vaultOptions.mount=Laufwerk
|
vaultOptions.mount=Laufwerk
|
||||||
vaultOptions.mount.readonly=Schreibgeschützt
|
vaultOptions.mount.readonly=Schreibgeschützt
|
||||||
@@ -184,12 +187,20 @@ vaultOptions.mount.mountPoint.driveLetter=Zugewiesenen Laufwerksbuchstaben verwe
|
|||||||
vaultOptions.mount.mountPoint.custom=Eigener Pfad
|
vaultOptions.mount.mountPoint.custom=Eigener Pfad
|
||||||
vaultOptions.mount.mountPoint.directoryPickerButton=Durchsuchen …
|
vaultOptions.mount.mountPoint.directoryPickerButton=Durchsuchen …
|
||||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Wähle ein leeres Verzeichnis
|
vaultOptions.mount.mountPoint.directoryPickerTitle=Wähle ein leeres Verzeichnis
|
||||||
|
## Master Key
|
||||||
|
vaultOptions.masterkey=Passwort
|
||||||
|
vaultOptions.masterkey.changePasswordBtn=Password ändern
|
||||||
|
vaultOptions.masterkey.recoveryKeyExpanation=Bei Verlust deines Passworts ist ein Wiederherstellungsschlüssel deine einzige Möglichkeit, den Zugriff auf einen Tresor wiederherzustellen.
|
||||||
|
vaultOptions.masterkey.showRecoveryKeyBtn=Wiederherstellungsschlüssel anzeigen
|
||||||
|
vaultOptions.masterkey.recoverPasswordBtn=Passwort wiederherstellen
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
recoveryKey.title=Wiederherstellungsschlüssel
|
recoveryKey.title=Wiederherstellungsschlüssel
|
||||||
recoveryKey.enterPassword.prompt=Gib dein Passwort ein, um den Wiederherstellungsschlüssel für „%s“ anzuzeigen:
|
recoveryKey.enterPassword.prompt=Gib dein Passwort ein, um den Wiederherstellungsschlüssel für „%s“ anzuzeigen:
|
||||||
recoveryKey.display.message=Mit folgendem Wiederherstellungsschlüssel kannst du den Zugriff auf „%s“ wiederherstellen:
|
recoveryKey.display.message=Mit folgendem Wiederherstellungsschlüssel kannst du den Zugriff auf „%s“ wiederherstellen:
|
||||||
recoveryKey.display.StorageHints=Bewahre ihn möglichst sicher auf, z. B.\n • in einem Passwortmanager\n • auf einem USB-Speicherstick\n • auf Papier ausgedruckt
|
recoveryKey.display.StorageHints=Bewahre ihn möglichst sicher auf, z. B.\n • in einem Passwortmanager\n • auf einem USB-Speicherstick\n • auf Papier ausgedruckt
|
||||||
|
recoveryKey.recover.prompt=Gib deinen Wiederherstellungsschlüssel für „%s“ ein:
|
||||||
|
recoveryKey.recover.validKey=Dies ist ein gültiger Wiederherstellungsschlüssel
|
||||||
|
|
||||||
# New Password
|
# New Password
|
||||||
newPassword.promptText=Gib ein neues Passwort ein
|
newPassword.promptText=Gib ein neues Passwort ein
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
## Start
|
## Start
|
||||||
## Run
|
## Run
|
||||||
## Sucess
|
## Sucess
|
||||||
|
## Missing file system capabilities
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
## General
|
## General
|
||||||
@@ -50,6 +51,7 @@
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
## Mount
|
## Mount
|
||||||
|
## Master Key
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ generic.button.back=Volver
|
|||||||
generic.button.cancel=Cancelar
|
generic.button.cancel=Cancelar
|
||||||
generic.button.change=Modificar
|
generic.button.change=Modificar
|
||||||
generic.button.copy=Copiar
|
generic.button.copy=Copiar
|
||||||
|
generic.button.copied=¡Copiado!
|
||||||
generic.button.done=Hecho
|
generic.button.done=Hecho
|
||||||
generic.button.next=Continuar
|
generic.button.next=Continuar
|
||||||
generic.button.print=Imprimir
|
generic.button.print=Imprimir
|
||||||
@@ -48,12 +49,9 @@ addvaultwizard.new.generateRecoveryKeyChoice=No podrá acceder a sus datos sin s
|
|||||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Sí, por favor
|
addvaultwizard.new.generateRecoveryKeyChoice.yes=Sí, por favor
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice.no=No, gracias
|
addvaultwizard.new.generateRecoveryKeyChoice.no=No, gracias
|
||||||
### Information
|
### Information
|
||||||
addvault.new.readme.storageLocation.fileName=QUÉ ES ESTE DIRECTORIO.rtf
|
|
||||||
addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ ARCHIVOS DE LA BÓVEDA ⚠️
|
|
||||||
addvault.new.readme.storageLocation.2=Esta es la ubicación del almacenamiento de su bóveda. {\\b NO} altere ningún archivo dentro de este directorio.
|
addvault.new.readme.storageLocation.2=Esta es la ubicación del almacenamiento de su bóveda. {\\b NO} altere ningún archivo dentro de este directorio.
|
||||||
addvault.new.readme.storageLocation.3=Si se desea cifrar archivos usando Cryptomator, desbloquear la bóveda y utilizar la unidad proporcionada.
|
addvault.new.readme.storageLocation.3=Si se desea cifrar archivos usando Cryptomator, desbloquear la bóveda y utilizar la unidad proporcionada.
|
||||||
addvault.new.readme.storageLocation.4=Si se necesita ayuda, probar %s.
|
addvault.new.readme.storageLocation.4=Si se necesita ayuda, probar %s.
|
||||||
addvault.new.readme.accessLocation.fileName=BIENVENIDO A SU BÓVEDA.rtf
|
|
||||||
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ VOLUMEN CIFRADO 🔐️
|
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ VOLUMEN CIFRADO 🔐️
|
||||||
addvault.new.readme.accessLocation.2=Esta es el lugar de acceso de la bóveda. Cualquier archivo añadido a este volumen será cifrado por Cryptomator. Para acceder a este volumen en otro momento, es necesario desbloquearlo otra vez desde la aplicación de Cryptomator.
|
addvault.new.readme.accessLocation.2=Esta es el lugar de acceso de la bóveda. Cualquier archivo añadido a este volumen será cifrado por Cryptomator. Para acceder a este volumen en otro momento, es necesario desbloquearlo otra vez desde la aplicación de Cryptomator.
|
||||||
addvault.new.readme.accessLocation.3=No dude en eliminar este archivo.
|
addvault.new.readme.accessLocation.3=No dude en eliminar este archivo.
|
||||||
@@ -103,6 +101,11 @@ migration.run.startMigrationBtn=Migrar bóveda
|
|||||||
## Sucess
|
## Sucess
|
||||||
migration.success.nextStepsInstructions="%s" se migró con éxito.\nYa se puede desbloquear la bóveda.
|
migration.success.nextStepsInstructions="%s" se migró con éxito.\nYa se puede desbloquear la bóveda.
|
||||||
migration.success.unlockNow=Desbloquear ahora
|
migration.success.unlockNow=Desbloquear ahora
|
||||||
|
## Missing file system capabilities
|
||||||
|
migration.error.missingFileSystemCapabilities.title=Sistema de archivos no soportado
|
||||||
|
migration.error.missingFileSystemCapabilities.description=La migración no se inició porque la bóveda se encuentra en un sistema de archivos inadecuado.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=El sistema de archivos no soporta nombres largos.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=El sistema de archivos no soporta rutas largas.
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
preferences.title=Preferencias
|
preferences.title=Preferencias
|
||||||
@@ -134,6 +137,7 @@ preferences.donationKey.getDonationKey=Obtener clave de donación
|
|||||||
|
|
||||||
# Main Window
|
# Main Window
|
||||||
main.closeBtn.tooltip=Cerrar
|
main.closeBtn.tooltip=Cerrar
|
||||||
|
main.minimizeBtn.tooltip=Minimizar
|
||||||
main.preferencesBtn.tooltip=Preferencias
|
main.preferencesBtn.tooltip=Preferencias
|
||||||
main.donationKeyMissing.tooltip=Por favor, considera donar
|
main.donationKeyMissing.tooltip=Por favor, considera donar
|
||||||
## Drag 'n' Drop
|
## Drag 'n' Drop
|
||||||
@@ -170,8 +174,7 @@ wrongFileAlert.information=Se ha intentado añadir un archivo o carpeta que no p
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
vaultOptions.general=General
|
vaultOptions.general=General
|
||||||
vaultOptions.general.changePasswordBtn=Cambiar contraseña
|
vaultOptions.general.unlockAfterStartup=Desbloquear bóveda al iniciar Cryptomator
|
||||||
vaultOptions.general.showRecoveryKeyBtn=Mostrar la clave de recuperación
|
|
||||||
## Mount
|
## Mount
|
||||||
vaultOptions.mount=Montaje
|
vaultOptions.mount=Montaje
|
||||||
vaultOptions.mount.readonly=Sólo lectura
|
vaultOptions.mount.readonly=Sólo lectura
|
||||||
@@ -184,12 +187,20 @@ vaultOptions.mount.mountPoint.driveLetter=Usar letra de unidad asignada
|
|||||||
vaultOptions.mount.mountPoint.custom=Ruta personalizada
|
vaultOptions.mount.mountPoint.custom=Ruta personalizada
|
||||||
vaultOptions.mount.mountPoint.directoryPickerButton=Elegir…
|
vaultOptions.mount.mountPoint.directoryPickerButton=Elegir…
|
||||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Elegir un directorio vacío
|
vaultOptions.mount.mountPoint.directoryPickerTitle=Elegir un directorio vacío
|
||||||
|
## Master Key
|
||||||
|
vaultOptions.masterkey=Contraseña
|
||||||
|
vaultOptions.masterkey.changePasswordBtn=Cambiar contraseña
|
||||||
|
vaultOptions.masterkey.recoveryKeyExpanation=Una clave de recuperación es el único medio para restaurar el acceso a una bóveda si pierde su contraseña.
|
||||||
|
vaultOptions.masterkey.showRecoveryKeyBtn=Mostrar clave de recuperación
|
||||||
|
vaultOptions.masterkey.recoverPasswordBtn=Recuperar contraseña
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
recoveryKey.title=Clave de recuperación
|
recoveryKey.title=Clave de recuperación
|
||||||
recoveryKey.enterPassword.prompt=Ingresar la contraseña para mostrar la clave de recuperación para "%s":
|
recoveryKey.enterPassword.prompt=Ingresar la contraseña para mostrar la clave de recuperación para "%s":
|
||||||
recoveryKey.display.message=La siguiente clave de recuperación puede usarse para restaurar el acceso a "%s":
|
recoveryKey.display.message=La siguiente clave de recuperación puede usarse para restaurar el acceso a "%s":
|
||||||
recoveryKey.display.StorageHints=Manténgala en algún lugar seguro, p.ej.:\n • Almacenarla en un administrador de contraseñas\n • Guardarla en una llave USB\n • Imprimirla en un papel
|
recoveryKey.display.StorageHints=Manténgala en algún lugar seguro, p.ej.:\n • Almacenarla en un administrador de contraseñas\n • Guardarla en una llave USB\n • Imprimirla en un papel
|
||||||
|
recoveryKey.recover.prompt=Ingresar la clave de recuperación para "%s":
|
||||||
|
recoveryKey.recover.validKey=Esta es una clave de recuperación válida
|
||||||
|
|
||||||
# New Password
|
# New Password
|
||||||
newPassword.promptText=Ingrese una contraseña nueva
|
newPassword.promptText=Ingrese una contraseña nueva
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ generic.button.back=Précédent
|
|||||||
generic.button.cancel=Annuler
|
generic.button.cancel=Annuler
|
||||||
generic.button.change=Modifier
|
generic.button.change=Modifier
|
||||||
generic.button.copy=Copier
|
generic.button.copy=Copier
|
||||||
|
generic.button.copied=Copié !
|
||||||
generic.button.done=Terminé
|
generic.button.done=Terminé
|
||||||
generic.button.next=Suivant
|
generic.button.next=Suivant
|
||||||
generic.button.print=Imprimer
|
generic.button.print=Imprimer
|
||||||
@@ -48,12 +49,9 @@ addvaultwizard.new.generateRecoveryKeyChoice=Sans votre mot de passe, il sera d'
|
|||||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Oui, mieux vaut prévenir que guérir
|
addvaultwizard.new.generateRecoveryKeyChoice.yes=Oui, mieux vaut prévenir que guérir
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice.no=Non merci, je n'oublierai pas mon mot de passe
|
addvaultwizard.new.generateRecoveryKeyChoice.no=Non merci, je n'oublierai pas mon mot de passe
|
||||||
### Information
|
### Information
|
||||||
addvault.new.readme.storageLocation.fileName=QUEL EST CE RÉPERTOIRE.rtf
|
|
||||||
addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ FICHIERS DU COFFRE ⚠️
|
|
||||||
addvault.new.readme.storageLocation.2=Ceci est l'emplacement de stockage de votre coffre. {\\b NE PAS} modifier les fichiers dans ce répertoire.
|
addvault.new.readme.storageLocation.2=Ceci est l'emplacement de stockage de votre coffre. {\\b NE PAS} modifier les fichiers dans ce répertoire.
|
||||||
addvault.new.readme.storageLocation.3=Si vous voulez chiffrer des fichiers en utilisant Cryptomator, déverrouillez le coffre et utilisez le lecteur fourni.
|
addvault.new.readme.storageLocation.3=Si vous voulez chiffrer des fichiers en utilisant Cryptomator, déverrouillez le coffre et utilisez le lecteur fourni.
|
||||||
addvault.new.readme.storageLocation.4=Si vous avez besoin d'aide, essayez %s.
|
addvault.new.readme.storageLocation.4=Si vous avez besoin d'aide, essayez %s.
|
||||||
addvault.new.readme.accessLocation.fileName=BIENVENUE DANS VOTRE COFFRE.rft
|
|
||||||
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ VOLUME CHIFFRÉ 🔐️
|
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ VOLUME CHIFFRÉ 🔐️
|
||||||
addvault.new.readme.accessLocation.2=Ceci est l'emplacement d'accès à votre coffre. Tous les fichiers ajoutés à ce volume seront chiffrés par Cryptomator. Pour accéder à ce volume plus tard, il suffit de le déverrouiller de nouveau à partir de l'application Cryptomator.
|
addvault.new.readme.accessLocation.2=Ceci est l'emplacement d'accès à votre coffre. Tous les fichiers ajoutés à ce volume seront chiffrés par Cryptomator. Pour accéder à ce volume plus tard, il suffit de le déverrouiller de nouveau à partir de l'application Cryptomator.
|
||||||
addvault.new.readme.accessLocation.3=Vous pouvez supprimer ce fichier.
|
addvault.new.readme.accessLocation.3=Vous pouvez supprimer ce fichier.
|
||||||
@@ -103,6 +101,11 @@ migration.run.startMigrationBtn=Migrer le coffre
|
|||||||
## Sucess
|
## Sucess
|
||||||
migration.success.nextStepsInstructions=“%s” migré.\nVous pouvez à présent déverrouiller ce coffre.
|
migration.success.nextStepsInstructions=“%s” migré.\nVous pouvez à présent déverrouiller ce coffre.
|
||||||
migration.success.unlockNow=Déverouiller
|
migration.success.unlockNow=Déverouiller
|
||||||
|
## Missing file system capabilities
|
||||||
|
migration.error.missingFileSystemCapabilities.title=Système de fichiers non pris en charge
|
||||||
|
migration.error.missingFileSystemCapabilities.description=La migration n'a pas débuté car votre coffre se trouve dans un système de fichiers inapproprié.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=Ce système de fichiers ne prend pas en charge les noms de fichiers longs.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=Ce système de fichiers ne prend pas en charge les chemins d'accès longs.
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
preferences.title=Préférences
|
preferences.title=Préférences
|
||||||
@@ -134,6 +137,7 @@ preferences.donationKey.getDonationKey=Obtenir une clé de don
|
|||||||
|
|
||||||
# Main Window
|
# Main Window
|
||||||
main.closeBtn.tooltip=Fermer
|
main.closeBtn.tooltip=Fermer
|
||||||
|
main.minimizeBtn.tooltip=Réduire
|
||||||
main.preferencesBtn.tooltip=Préférences
|
main.preferencesBtn.tooltip=Préférences
|
||||||
main.donationKeyMissing.tooltip=Merci d'envisager un don
|
main.donationKeyMissing.tooltip=Merci d'envisager un don
|
||||||
## Drag 'n' Drop
|
## Drag 'n' Drop
|
||||||
@@ -170,8 +174,7 @@ wrongFileAlert.information=Vous avez essayé d’ajouter un fichier ou un dossie
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
vaultOptions.general=Général
|
vaultOptions.general=Général
|
||||||
vaultOptions.general.changePasswordBtn=Modifier le mot de passe
|
vaultOptions.general.unlockAfterStartup=Déverrouiller le coffre au démarrage
|
||||||
vaultOptions.general.showRecoveryKeyBtn=Afficher la clé de récupération
|
|
||||||
## Mount
|
## Mount
|
||||||
vaultOptions.mount=Montage
|
vaultOptions.mount=Montage
|
||||||
vaultOptions.mount.readonly=Lecture seule
|
vaultOptions.mount.readonly=Lecture seule
|
||||||
@@ -184,12 +187,20 @@ vaultOptions.mount.mountPoint.driveLetter=Utiliser la lettre du lecteur assigné
|
|||||||
vaultOptions.mount.mountPoint.custom=Chemin personnalisé
|
vaultOptions.mount.mountPoint.custom=Chemin personnalisé
|
||||||
vaultOptions.mount.mountPoint.directoryPickerButton=Choisir...
|
vaultOptions.mount.mountPoint.directoryPickerButton=Choisir...
|
||||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Choisir un répertoire vide
|
vaultOptions.mount.mountPoint.directoryPickerTitle=Choisir un répertoire vide
|
||||||
|
## Master Key
|
||||||
|
vaultOptions.masterkey=Mot de passe
|
||||||
|
vaultOptions.masterkey.changePasswordBtn=Modifier le mot de passe
|
||||||
|
vaultOptions.masterkey.recoveryKeyExpanation=Une clé de récupération est votre seul moyen de rétablir l'accès à un coffre-fort, si vous perdez votre mot de passe.
|
||||||
|
vaultOptions.masterkey.showRecoveryKeyBtn=Afficher la clé de récupération
|
||||||
|
vaultOptions.masterkey.recoverPasswordBtn=Récupération du mot de passe
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
recoveryKey.title=Clé de récupération
|
recoveryKey.title=Clé de récupération
|
||||||
recoveryKey.enterPassword.prompt=Entrez votre mot de passe pour afficher la clé de récupération pour "%s":
|
recoveryKey.enterPassword.prompt=Entrez votre mot de passe pour afficher la clé de récupération pour "%s":
|
||||||
recoveryKey.display.message=La clé de récupération suivante peut être utilisée pour restaurer l'accès à "%s " :
|
recoveryKey.display.message=La clé de récupération suivante peut être utilisée pour restaurer l'accès à "%s " :
|
||||||
recoveryKey.display.StorageHints=Gardez-le quelque part de façon très sûr, par ex. :\n • Stockez le en utilisant un gestionnaire de mot de passe\n • Enregistrez-le sur une clé USB\n • Imprimez-le sur papier
|
recoveryKey.display.StorageHints=Gardez-le quelque part de façon très sûr, par ex. :\n • Stockez le en utilisant un gestionnaire de mot de passe\n • Enregistrez-le sur une clé USB\n • Imprimez-le sur papier
|
||||||
|
recoveryKey.recover.prompt=Entrez votre clé de récupération pour "%s":
|
||||||
|
recoveryKey.recover.validKey=Cette clé de récupération est valide
|
||||||
|
|
||||||
# New Password
|
# New Password
|
||||||
newPassword.promptText=Entrer un nouveau mot de passe
|
newPassword.promptText=Entrer un nouveau mot de passe
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
## Start
|
## Start
|
||||||
## Run
|
## Run
|
||||||
## Sucess
|
## Sucess
|
||||||
|
## Missing file system capabilities
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
## General
|
## General
|
||||||
@@ -50,6 +51,7 @@
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
## Mount
|
## Mount
|
||||||
|
## Master Key
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ generic.button.back=Indietro
|
|||||||
generic.button.cancel=Annulla
|
generic.button.cancel=Annulla
|
||||||
generic.button.change=Modifica
|
generic.button.change=Modifica
|
||||||
generic.button.copy=Copia
|
generic.button.copy=Copia
|
||||||
|
generic.button.copied=Copiato!
|
||||||
generic.button.done=Fatto
|
generic.button.done=Fatto
|
||||||
generic.button.next=Avanti
|
generic.button.next=Avanti
|
||||||
generic.button.print=Stampa
|
generic.button.print=Stampa
|
||||||
@@ -48,12 +49,9 @@ addvaultwizard.new.generateRecoveryKeyChoice=Non sarai in grado di accedere ai t
|
|||||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Si, per favore, è meglio essere al sicuro
|
addvaultwizard.new.generateRecoveryKeyChoice.yes=Si, per favore, è meglio essere al sicuro
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice.no=No grazie, non perderò la mia password
|
addvaultwizard.new.generateRecoveryKeyChoice.no=No grazie, non perderò la mia password
|
||||||
### Information
|
### Information
|
||||||
addvault.new.readme.storageLocation.fileName=CHE COSA È QUESTA DIRECTORY.rtf
|
|
||||||
addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ FILE CASSAFORTE ⚠️
|
|
||||||
addvault.new.readme.storageLocation.2=Questa è la posizione di archiviazione della tua cassaforte. {\\b NON} modifica qualsiasi file all'interno di questa cartella.
|
addvault.new.readme.storageLocation.2=Questa è la posizione di archiviazione della tua cassaforte. {\\b NON} modifica qualsiasi file all'interno di questa cartella.
|
||||||
addvault.new.readme.storageLocation.3=Se vuoi crittografare i file usando Cryptomator, sblocca la cassaforte e usa l'unità fornita.
|
addvault.new.readme.storageLocation.3=Se vuoi crittografare i file usando Cryptomator, sblocca la cassaforte e usa l'unità fornita.
|
||||||
addvault.new.readme.storageLocation.4=Se hai bisogno di aiuto, prova %s.
|
addvault.new.readme.storageLocation.4=Se hai bisogno di aiuto, prova %s.
|
||||||
addvault.new.readme.accessLocation.fileName=BENVENUTO NELLA TUA CASSAFORTE.rtf
|
|
||||||
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ VOLUME CRIPTATO 🔐️
|
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ VOLUME CRIPTATO 🔐️
|
||||||
addvault.new.readme.accessLocation.2=Questa è la posizione di accesso del tuo caveau. Tutti i file aggiunti a questo volume verranno crittografati da Cryptomator. Per accedere a questo volume in un secondo momento, è sufficiente sbloccarlo nuovamente dall'applicazione Cryptomator.
|
addvault.new.readme.accessLocation.2=Questa è la posizione di accesso del tuo caveau. Tutti i file aggiunti a questo volume verranno crittografati da Cryptomator. Per accedere a questo volume in un secondo momento, è sufficiente sbloccarlo nuovamente dall'applicazione Cryptomator.
|
||||||
addvault.new.readme.accessLocation.3=Sei libero di rimuovere questo file.
|
addvault.new.readme.accessLocation.3=Sei libero di rimuovere questo file.
|
||||||
@@ -103,6 +101,7 @@ migration.run.startMigrationBtn=Migra Cassaforte
|
|||||||
## Sucess
|
## Sucess
|
||||||
migration.success.nextStepsInstructions=Migrato "%s" con successo.\nOra puoi sbloccare la tua cassaforte.
|
migration.success.nextStepsInstructions=Migrato "%s" con successo.\nOra puoi sbloccare la tua cassaforte.
|
||||||
migration.success.unlockNow=Sblocca adesso
|
migration.success.unlockNow=Sblocca adesso
|
||||||
|
## Missing file system capabilities
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
preferences.title=Impostazioni
|
preferences.title=Impostazioni
|
||||||
@@ -133,6 +132,7 @@ preferences.donationKey.getDonationKey=Ottieni una chiave di donazione
|
|||||||
|
|
||||||
# Main Window
|
# Main Window
|
||||||
main.closeBtn.tooltip=Chiudi
|
main.closeBtn.tooltip=Chiudi
|
||||||
|
main.minimizeBtn.tooltip=Minimizza
|
||||||
main.preferencesBtn.tooltip=Impostazioni
|
main.preferencesBtn.tooltip=Impostazioni
|
||||||
main.donationKeyMissing.tooltip=Per favore considera una donazione
|
main.donationKeyMissing.tooltip=Per favore considera una donazione
|
||||||
## Drag 'n' Drop
|
## Drag 'n' Drop
|
||||||
@@ -169,8 +169,7 @@ wrongFileAlert.information=Hai provato ad aggiungere un file o una cartella che
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
vaultOptions.general=Generali
|
vaultOptions.general=Generali
|
||||||
vaultOptions.general.changePasswordBtn=Modifica password
|
vaultOptions.general.unlockAfterStartup=Sblocca vault all'avvio di Cryptomator
|
||||||
vaultOptions.general.showRecoveryKeyBtn=Visualizza la chiave di recupero
|
|
||||||
## Mount
|
## Mount
|
||||||
vaultOptions.mount=Montaggio
|
vaultOptions.mount=Montaggio
|
||||||
vaultOptions.mount.readonly=Sola lettura
|
vaultOptions.mount.readonly=Sola lettura
|
||||||
@@ -183,12 +182,18 @@ vaultOptions.mount.mountPoint.driveLetter=Usa la lettera del drive assegnata
|
|||||||
vaultOptions.mount.mountPoint.custom=Percorso personalizzato
|
vaultOptions.mount.mountPoint.custom=Percorso personalizzato
|
||||||
vaultOptions.mount.mountPoint.directoryPickerButton=Scegli…
|
vaultOptions.mount.mountPoint.directoryPickerButton=Scegli…
|
||||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Scegli una directory vuota
|
vaultOptions.mount.mountPoint.directoryPickerTitle=Scegli una directory vuota
|
||||||
|
## Master Key
|
||||||
|
vaultOptions.masterkey=Password
|
||||||
|
vaultOptions.masterkey.changePasswordBtn=Modifica password
|
||||||
|
vaultOptions.masterkey.recoverPasswordBtn=Recupera password
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
recoveryKey.title=Chiave di recupero
|
recoveryKey.title=Chiave di recupero
|
||||||
recoveryKey.enterPassword.prompt=Inserisci la password per visualizzare la chiave di recupero per "%s":
|
recoveryKey.enterPassword.prompt=Inserisci la password per visualizzare la chiave di recupero per "%s":
|
||||||
recoveryKey.display.message=La seguente chiave di recupero può essere utilizzata per ripristinare l'accesso a %s":
|
recoveryKey.display.message=La seguente chiave di recupero può essere utilizzata per ripristinare l'accesso a %s":
|
||||||
recoveryKey.display.StorageHints=Mantienilo da qualche parte molto sicuro, ad es.\n • Archivialo usando un gestore di password\n • Salvarlo su un'unità flash USB\n • Stamparlo su carta
|
recoveryKey.display.StorageHints=Mantienilo da qualche parte molto sicuro, ad es.\n • Archivialo usando un gestore di password\n • Salvarlo su un'unità flash USB\n • Stamparlo su carta
|
||||||
|
recoveryKey.recover.prompt=Inserisci la tua chiave di recupero per "%s":
|
||||||
|
recoveryKey.recover.validKey=Questa è una chiave di recupero valida
|
||||||
|
|
||||||
# New Password
|
# New Password
|
||||||
newPassword.promptText=Inserisci una nuova password
|
newPassword.promptText=Inserisci una nuova password
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ generic.button.back=戻る
|
|||||||
generic.button.cancel=キャンセル
|
generic.button.cancel=キャンセル
|
||||||
generic.button.change=変更
|
generic.button.change=変更
|
||||||
generic.button.copy=コピー
|
generic.button.copy=コピー
|
||||||
|
generic.button.copied=コピー完了!
|
||||||
generic.button.done=完了
|
generic.button.done=完了
|
||||||
generic.button.next=次へ
|
generic.button.next=次へ
|
||||||
generic.button.print=印刷
|
generic.button.print=印刷
|
||||||
@@ -48,12 +49,9 @@ addvaultwizard.new.generateRecoveryKeyChoice=データにアクセスするに
|
|||||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=はい、後悔するより手段を用意します
|
addvaultwizard.new.generateRecoveryKeyChoice.yes=はい、後悔するより手段を用意します
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice.no=いいえ、パスワードをなくしません
|
addvaultwizard.new.generateRecoveryKeyChoice.no=いいえ、パスワードをなくしません
|
||||||
### Information
|
### Information
|
||||||
addvault.new.readme.storageLocation.fileName=このディレクトリについて.rtf
|
|
||||||
addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ 金庫ファイル ⚠️
|
|
||||||
addvault.new.readme.storageLocation.2=ここは金庫のストレージ先です。このディレクトリーのいずれに対しても変更を、{\\b 加えないでください} 。
|
addvault.new.readme.storageLocation.2=ここは金庫のストレージ先です。このディレクトリーのいずれに対しても変更を、{\\b 加えないでください} 。
|
||||||
addvault.new.readme.storageLocation.3=Cryptomator を利用してファイルを暗号化するには、金庫を施錠して表示されるドライブを使用してください。
|
addvault.new.readme.storageLocation.3=Cryptomator を利用してファイルを暗号化するには、金庫を施錠して表示されるドライブを使用してください。
|
||||||
addvault.new.readme.storageLocation.4=ヘルプが必要であれば %s をお試しください。
|
addvault.new.readme.storageLocation.4=ヘルプが必要であれば %s をお試しください。
|
||||||
addvault.new.readme.accessLocation.fileName=金庫へようこそ.rtf
|
|
||||||
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ 暗号ボリューム 🔐️
|
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ 暗号ボリューム 🔐️
|
||||||
addvault.new.readme.accessLocation.2=ここは、金庫のアクセス先です。Cryptomator によりこのボリュームに追加したファイルが暗号化されます。これからは Cryptomator アプリケーションから解錠してこのボリュームにアクセスしてください。
|
addvault.new.readme.accessLocation.2=ここは、金庫のアクセス先です。Cryptomator によりこのボリュームに追加したファイルが暗号化されます。これからは Cryptomator アプリケーションから解錠してこのボリュームにアクセスしてください。
|
||||||
addvault.new.readme.accessLocation.3=このファイルはいつでも削除できます。
|
addvault.new.readme.accessLocation.3=このファイルはいつでも削除できます。
|
||||||
@@ -103,6 +101,11 @@ migration.run.startMigrationBtn=金庫を移行
|
|||||||
## Sucess
|
## Sucess
|
||||||
migration.success.nextStepsInstructions="%s" の移行が成功しました。\n金庫を解錠できるようになりました。
|
migration.success.nextStepsInstructions="%s" の移行が成功しました。\n金庫を解錠できるようになりました。
|
||||||
migration.success.unlockNow=今すぐ解錠
|
migration.success.unlockNow=今すぐ解錠
|
||||||
|
## Missing file system capabilities
|
||||||
|
migration.error.missingFileSystemCapabilities.title=サポートされないファイルタイプ
|
||||||
|
migration.error.missingFileSystemCapabilities.description=金庫が不適切なファイルシステムにあるため、移行が開始されませんでした。
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=ファイルシステムが長いファイル名をサポートしていません。
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=ファイルシステムが長いパスをサポートしていません。
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
preferences.title=設定
|
preferences.title=設定
|
||||||
@@ -134,6 +137,7 @@ preferences.donationKey.getDonationKey=Donation Key を入手する
|
|||||||
|
|
||||||
# Main Window
|
# Main Window
|
||||||
main.closeBtn.tooltip=閉じる
|
main.closeBtn.tooltip=閉じる
|
||||||
|
main.minimizeBtn.tooltip=最小化
|
||||||
main.preferencesBtn.tooltip=設定
|
main.preferencesBtn.tooltip=設定
|
||||||
main.donationKeyMissing.tooltip=寄付をお願いします
|
main.donationKeyMissing.tooltip=寄付をお願いします
|
||||||
## Drag 'n' Drop
|
## Drag 'n' Drop
|
||||||
@@ -170,8 +174,7 @@ wrongFileAlert.information=Cryptomator の金庫ではないファイルかフ
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
vaultOptions.general=基本設定
|
vaultOptions.general=基本設定
|
||||||
vaultOptions.general.changePasswordBtn=パスワードの変更
|
vaultOptions.general.unlockAfterStartup=Cryptomatorの起動時に金庫を解錠する
|
||||||
vaultOptions.general.showRecoveryKeyBtn=回復キーを表示
|
|
||||||
## Mount
|
## Mount
|
||||||
vaultOptions.mount=マウント
|
vaultOptions.mount=マウント
|
||||||
vaultOptions.mount.readonly=読み取り専用
|
vaultOptions.mount.readonly=読み取り専用
|
||||||
@@ -184,12 +187,20 @@ vaultOptions.mount.mountPoint.driveLetter=割り当てられるドライブ文
|
|||||||
vaultOptions.mount.mountPoint.custom=カスタムパス
|
vaultOptions.mount.mountPoint.custom=カスタムパス
|
||||||
vaultOptions.mount.mountPoint.directoryPickerButton=選択...
|
vaultOptions.mount.mountPoint.directoryPickerButton=選択...
|
||||||
vaultOptions.mount.mountPoint.directoryPickerTitle=空のディレクトリを選択してください
|
vaultOptions.mount.mountPoint.directoryPickerTitle=空のディレクトリを選択してください
|
||||||
|
## Master Key
|
||||||
|
vaultOptions.masterkey=パスワード
|
||||||
|
vaultOptions.masterkey.changePasswordBtn=パスワードの変更
|
||||||
|
vaultOptions.masterkey.recoveryKeyExpanation=回復キーはパスワードを忘れてしまった場合でも、金庫へのアクセスを回復する唯一の手段です。
|
||||||
|
vaultOptions.masterkey.showRecoveryKeyBtn=回復キーを表示
|
||||||
|
vaultOptions.masterkey.recoverPasswordBtn=パスワードの回復
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
recoveryKey.title=回復キー
|
recoveryKey.title=回復キー
|
||||||
recoveryKey.enterPassword.prompt="%s" の回復キーを表示するためのパスワードを入力してください:
|
recoveryKey.enterPassword.prompt="%s" の回復キーを表示するためのパスワードを入力してください:
|
||||||
recoveryKey.display.message="%s" へのアクセス権限を復元するリカバリーキー:
|
recoveryKey.display.message="%s" へのアクセス権限を復元するリカバリーキー:
|
||||||
recoveryKey.display.StorageHints=とても安全な場所に保存してください、例えば:\n • パスワード管理ソフトに保存\n • USB フラッシュドライブに保存\n • 紙に印刷
|
recoveryKey.display.StorageHints=とても安全な場所に保存してください、例えば:\n • パスワード管理ソフトに保存\n • USB フラッシュドライブに保存\n • 紙に印刷
|
||||||
|
recoveryKey.recover.prompt="%s" の回復キーを入力してください:
|
||||||
|
recoveryKey.recover.validKey=有効な回復キー
|
||||||
|
|
||||||
# New Password
|
# New Password
|
||||||
newPassword.promptText=新しいパスワードを入力してください
|
newPassword.promptText=新しいパスワードを入力してください
|
||||||
|
|||||||
@@ -48,12 +48,9 @@ addvaultwizard.new.generateRecoveryKeyChoice=비밀번호가 없으면 데이터
|
|||||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=네. 미안한 것 보단 안전함이 더 낫습니다.
|
addvaultwizard.new.generateRecoveryKeyChoice.yes=네. 미안한 것 보단 안전함이 더 낫습니다.
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice.no=아니요, 나는 비밀번호를 잊지 않을겁니다.
|
addvaultwizard.new.generateRecoveryKeyChoice.no=아니요, 나는 비밀번호를 잊지 않을겁니다.
|
||||||
### Information
|
### Information
|
||||||
addvault.new.readme.storageLocation.fileName=이 디렉터리에 대해.rtf
|
|
||||||
addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ VAULT 파일 ⚠️
|
|
||||||
addvault.new.readme.storageLocation.2=이곳은 Vault 파일 저장경로입니다. {\\b 절대로} 이 디렉터리의 파일을 임의로 교체하거나 수정하지 마십시요.
|
addvault.new.readme.storageLocation.2=이곳은 Vault 파일 저장경로입니다. {\\b 절대로} 이 디렉터리의 파일을 임의로 교체하거나 수정하지 마십시요.
|
||||||
addvault.new.readme.storageLocation.3=Cryptomator를 사용하여 파일을 암호화하고 싶으시면, Vault를 잠금해제하고 준비된 드라이브에 사용하십시요.
|
addvault.new.readme.storageLocation.3=Cryptomator를 사용하여 파일을 암호화하고 싶으시면, Vault를 잠금해제하고 준비된 드라이브에 사용하십시요.
|
||||||
addvault.new.readme.storageLocation.4=도움이 필요하시면, %s를 시도하여 보십시요.
|
addvault.new.readme.storageLocation.4=도움이 필요하시면, %s를 시도하여 보십시요.
|
||||||
addvault.new.readme.accessLocation.fileName=Vault에 오신것을 환영합니다.rtf
|
|
||||||
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ 암호화된 볼륨 🔐️
|
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ 암호화된 볼륨 🔐️
|
||||||
addvault.new.readme.accessLocation.2=이곳은 Vault의 접근장소입니다. 이 볼륨에 추가된 파일은 Cryptomator로 암호화 될 것입니다. 나중에 이 볼륨에 접근하기 위해서는, 단순히 Cryptomator 애플리케이션 내에서 잠금해제하여 주십시요.
|
addvault.new.readme.accessLocation.2=이곳은 Vault의 접근장소입니다. 이 볼륨에 추가된 파일은 Cryptomator로 암호화 될 것입니다. 나중에 이 볼륨에 접근하기 위해서는, 단순히 Cryptomator 애플리케이션 내에서 잠금해제하여 주십시요.
|
||||||
addvault.new.readme.accessLocation.3=이 파일은 지우셔도 무방합니다.
|
addvault.new.readme.accessLocation.3=이 파일은 지우셔도 무방합니다.
|
||||||
@@ -103,6 +100,11 @@ migration.run.startMigrationBtn=Vault 마이그레이션
|
|||||||
## Sucess
|
## Sucess
|
||||||
migration.success.nextStepsInstructions="%s" 의 마이그레이션이 성공적으로 완료되었습니다. 이제 Vault를 잠금해제할 수 있습니다.
|
migration.success.nextStepsInstructions="%s" 의 마이그레이션이 성공적으로 완료되었습니다. 이제 Vault를 잠금해제할 수 있습니다.
|
||||||
migration.success.unlockNow=지금 잠금해제
|
migration.success.unlockNow=지금 잠금해제
|
||||||
|
## Missing file system capabilities
|
||||||
|
migration.error.missingFileSystemCapabilities.title=지원하지 않는 파일 시스템
|
||||||
|
migration.error.missingFileSystemCapabilities.description=Vault가 부적절한 파일시스템에 있기 때문에 마이그레이션이 시작되지 않았습니다.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=너무 긴 파일 이름을 파일시스템에서 지원하지 않습니다.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=너무 긴 경로를 파일시스템에서 지원하지 않습니다.
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
preferences.title=환경설정
|
preferences.title=환경설정
|
||||||
@@ -134,6 +136,7 @@ preferences.donationKey.getDonationKey=도네이션(기부) 키 얻기
|
|||||||
|
|
||||||
# Main Window
|
# Main Window
|
||||||
main.closeBtn.tooltip=닫기
|
main.closeBtn.tooltip=닫기
|
||||||
|
main.minimizeBtn.tooltip=최소화
|
||||||
main.preferencesBtn.tooltip=환경설정
|
main.preferencesBtn.tooltip=환경설정
|
||||||
main.donationKeyMissing.tooltip=기부를 부탁드립니다.
|
main.donationKeyMissing.tooltip=기부를 부탁드립니다.
|
||||||
## Drag 'n' Drop
|
## Drag 'n' Drop
|
||||||
@@ -170,8 +173,7 @@ wrongFileAlert.information=Cryptomator vault에 없는 파일이나 폴더를
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
vaultOptions.general=일반
|
vaultOptions.general=일반
|
||||||
vaultOptions.general.changePasswordBtn=비밀번호 변경
|
vaultOptions.general.unlockAfterStartup=Cryptomator를 시작할 때 Vault 잠금 해제
|
||||||
vaultOptions.general.showRecoveryKeyBtn=복구 키 출력
|
|
||||||
## Mount
|
## Mount
|
||||||
vaultOptions.mount=드라이브 구성
|
vaultOptions.mount=드라이브 구성
|
||||||
vaultOptions.mount.readonly=읽기 전용
|
vaultOptions.mount.readonly=읽기 전용
|
||||||
@@ -184,12 +186,18 @@ vaultOptions.mount.mountPoint.driveLetter=드라이브 문자를 지정하여
|
|||||||
vaultOptions.mount.mountPoint.custom=사용자 지정 경로
|
vaultOptions.mount.mountPoint.custom=사용자 지정 경로
|
||||||
vaultOptions.mount.mountPoint.directoryPickerButton=선택
|
vaultOptions.mount.mountPoint.directoryPickerButton=선택
|
||||||
vaultOptions.mount.mountPoint.directoryPickerTitle=빈 디렉터리를 선택
|
vaultOptions.mount.mountPoint.directoryPickerTitle=빈 디렉터리를 선택
|
||||||
|
## Master Key
|
||||||
|
vaultOptions.masterkey.changePasswordBtn=비밀번호 변경
|
||||||
|
vaultOptions.masterkey.recoveryKeyExpanation=복구 키는 비밀번호를 잊어버렸을 때, Vault의 접근을 복원 할 수 있는 유일한 방법입니다.
|
||||||
|
vaultOptions.masterkey.showRecoveryKeyBtn=복구 키 표시
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
recoveryKey.title=복구 키
|
recoveryKey.title=복구 키
|
||||||
recoveryKey.enterPassword.prompt="%s"의 복구 키를 표시하려면 비밀번호를 입력하여 주십시요.
|
recoveryKey.enterPassword.prompt="%s"의 복구 키를 표시하려면 비밀번호를 입력하여 주십시요.
|
||||||
recoveryKey.display.message="%s" 데이터 접근을 복원하는데 사용 할 수 있는 복구 키 입니다:
|
recoveryKey.display.message="%s" 데이터 접근을 복원하는데 사용 할 수 있는 복구 키 입니다:
|
||||||
recoveryKey.display.StorageHints=매우 안전한곳에 보관하십시요. 예시:\n • 비밀번호 관리자를 사용하여 저장\n • USB 플래시 드라이브에 저장\n • 종이에 출력
|
recoveryKey.display.StorageHints=매우 안전한곳에 보관하십시요. 예시:\n • 비밀번호 관리자를 사용하여 저장\n • USB 플래시 드라이브에 저장\n • 종이에 출력
|
||||||
|
recoveryKey.recover.prompt="%s"의 복구키를 입력하십시요:
|
||||||
|
recoveryKey.recover.validKey=올바른 복구 키 입니다.
|
||||||
|
|
||||||
# New Password
|
# New Password
|
||||||
newPassword.promptText=새 비밀번호를 입력하십시요.
|
newPassword.promptText=새 비밀번호를 입력하십시요.
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ generic.button.back=Terug
|
|||||||
generic.button.cancel=Annuleren
|
generic.button.cancel=Annuleren
|
||||||
generic.button.change=Wijzig
|
generic.button.change=Wijzig
|
||||||
generic.button.copy=Kopieer
|
generic.button.copy=Kopieer
|
||||||
|
generic.button.copied=Gekopieerd!
|
||||||
generic.button.done=Klaar
|
generic.button.done=Klaar
|
||||||
generic.button.next=Volgende
|
generic.button.next=Volgende
|
||||||
generic.button.print=Afdrukken
|
generic.button.print=Afdrukken
|
||||||
@@ -48,12 +49,9 @@ addvaultwizard.new.generateRecoveryKeyChoice=Zonder wachtwoord hebt u geen toega
|
|||||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Ja graag, voorkomen is beter dan genezen
|
addvaultwizard.new.generateRecoveryKeyChoice.yes=Ja graag, voorkomen is beter dan genezen
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice.no=Nee bedankt, ik verlies mijn wachtwoord niet
|
addvaultwizard.new.generateRecoveryKeyChoice.no=Nee bedankt, ik verlies mijn wachtwoord niet
|
||||||
### Information
|
### Information
|
||||||
addvault.new.readme.storageLocation.fileName=WAT IS DEZE MAP.rtf
|
|
||||||
addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ KLUIS BESTANDEN ⚠️
|
|
||||||
addvault.new.readme.storageLocation.2=Dit is de opslaglocatie van uw kluis. Wijzig {\\b GEEN} bestanden binnen deze map.
|
addvault.new.readme.storageLocation.2=Dit is de opslaglocatie van uw kluis. Wijzig {\\b GEEN} bestanden binnen deze map.
|
||||||
addvault.new.readme.storageLocation.3=Als u bestanden wilt versleutelen met Cryptomator, ontgrendel dan de kluis en gebruik de opgegeven schijf.
|
addvault.new.readme.storageLocation.3=Als u bestanden wilt versleutelen met Cryptomator, ontgrendel dan de kluis en gebruik de opgegeven schijf.
|
||||||
addvault.new.readme.storageLocation.4=Als je hulp nodig hebt, probeer %s.
|
addvault.new.readme.storageLocation.4=Als je hulp nodig hebt, probeer %s.
|
||||||
addvault.new.readme.accessLocation.fileName=WELKOM IN UW KLUIS.rtf
|
|
||||||
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐 VERSLEUTELD VOLUME 🔐
|
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐 VERSLEUTELD VOLUME 🔐
|
||||||
addvault.new.readme.accessLocation.2=Dit is de toegangslocatie van uw kluis. Alle bestanden die aan dit volume worden toegevoegd zullen worden versleuteld door Cryptomator. Om dit volume op een later tijdstip te kunnen gebruiken, ontgrendelt u het gewoon opnieuw vanuit de Cryptomator applicatie.
|
addvault.new.readme.accessLocation.2=Dit is de toegangslocatie van uw kluis. Alle bestanden die aan dit volume worden toegevoegd zullen worden versleuteld door Cryptomator. Om dit volume op een later tijdstip te kunnen gebruiken, ontgrendelt u het gewoon opnieuw vanuit de Cryptomator applicatie.
|
||||||
addvault.new.readme.accessLocation.3=Voel je vrij om dit bestand te verwijderen.
|
addvault.new.readme.accessLocation.3=Voel je vrij om dit bestand te verwijderen.
|
||||||
@@ -103,6 +101,11 @@ migration.run.startMigrationBtn=Kluis migreren
|
|||||||
## Sucess
|
## Sucess
|
||||||
migration.success.nextStepsInstructions="%s" is succesvol gemigreerd.\nU kunt nu uw kluis ontgrendelen.
|
migration.success.nextStepsInstructions="%s" is succesvol gemigreerd.\nU kunt nu uw kluis ontgrendelen.
|
||||||
migration.success.unlockNow=Nu Ontgrendelen
|
migration.success.unlockNow=Nu Ontgrendelen
|
||||||
|
## Missing file system capabilities
|
||||||
|
migration.error.missingFileSystemCapabilities.title=Niet ondersteund bestandssysteem
|
||||||
|
migration.error.missingFileSystemCapabilities.description=Migratie is niet gestart, omdat uw kluis zich op een niet-adequaat bestandssysteem bevindt.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=Het bestandssysteem ondersteunt geen lange bestandsnamen.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=Het bestandssysteem ondersteunt geen lange paden.
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
preferences.title=Voorkeuren
|
preferences.title=Voorkeuren
|
||||||
@@ -134,6 +137,7 @@ preferences.donationKey.getDonationKey=Verkrijg een donatiesleutel
|
|||||||
|
|
||||||
# Main Window
|
# Main Window
|
||||||
main.closeBtn.tooltip=Sluiten
|
main.closeBtn.tooltip=Sluiten
|
||||||
|
main.minimizeBtn.tooltip=Minimaliseer
|
||||||
main.preferencesBtn.tooltip=Voorkeuren
|
main.preferencesBtn.tooltip=Voorkeuren
|
||||||
main.donationKeyMissing.tooltip=Overweeg alstublieft om een donatie te doen
|
main.donationKeyMissing.tooltip=Overweeg alstublieft om een donatie te doen
|
||||||
## Drag 'n' Drop
|
## Drag 'n' Drop
|
||||||
@@ -170,8 +174,7 @@ wrongFileAlert.information=U hebt geprobeerd om een bestand of een map toe te vo
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
vaultOptions.general=Algemeen
|
vaultOptions.general=Algemeen
|
||||||
vaultOptions.general.changePasswordBtn=Wijzig wachtwoord
|
vaultOptions.general.unlockAfterStartup=Ontgrendel kluis bij het starten van Cryptomator
|
||||||
vaultOptions.general.showRecoveryKeyBtn=Toon herstelsleutel
|
|
||||||
## Mount
|
## Mount
|
||||||
vaultOptions.mount=Aankoppelen
|
vaultOptions.mount=Aankoppelen
|
||||||
vaultOptions.mount.readonly=Alleen-Lezen
|
vaultOptions.mount.readonly=Alleen-Lezen
|
||||||
@@ -184,12 +187,20 @@ vaultOptions.mount.mountPoint.driveLetter=Gebruik de toegewezen schijfletter
|
|||||||
vaultOptions.mount.mountPoint.custom=Aangepast pad
|
vaultOptions.mount.mountPoint.custom=Aangepast pad
|
||||||
vaultOptions.mount.mountPoint.directoryPickerButton=Kies…
|
vaultOptions.mount.mountPoint.directoryPickerButton=Kies…
|
||||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Kies een lege map
|
vaultOptions.mount.mountPoint.directoryPickerTitle=Kies een lege map
|
||||||
|
## Master Key
|
||||||
|
vaultOptions.masterkey=Wachtwoord
|
||||||
|
vaultOptions.masterkey.changePasswordBtn=Wijzig wachtwoord
|
||||||
|
vaultOptions.masterkey.recoveryKeyExpanation=Een herstelsleutel is je enige manier om de toegang tot een kluis te herstellen als je je wachtwoord kwijtraakt.
|
||||||
|
vaultOptions.masterkey.showRecoveryKeyBtn=Toon herstelsleutel
|
||||||
|
vaultOptions.masterkey.recoverPasswordBtn=Wachtwoord herstellen
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
recoveryKey.title=Herstelsleutel
|
recoveryKey.title=Herstelsleutel
|
||||||
recoveryKey.enterPassword.prompt=Voer uw wachtwoord in om de herstelsleutel voor "%s" te tonen:
|
recoveryKey.enterPassword.prompt=Voer uw wachtwoord in om de herstelsleutel voor "%s" te tonen:
|
||||||
recoveryKey.display.message=De volgende herstelsleutel kan worden gebruikt om "%s" te herstellen:
|
recoveryKey.display.message=De volgende herstelsleutel kan worden gebruikt om "%s" te herstellen:
|
||||||
recoveryKey.display.StorageHints=Bewaar het op een veilige plek, bv:\n • Bewaar het in een wachtwoordmanager\n • Sla het op op een USB-stick\n • Print het op papier
|
recoveryKey.display.StorageHints=Bewaar het op een veilige plek, bv:\n • Bewaar het in een wachtwoordmanager\n • Sla het op op een USB-stick\n • Print het op papier
|
||||||
|
recoveryKey.recover.prompt=Voer uw herstelsleutel in voor "%s":
|
||||||
|
recoveryKey.recover.validKey=Dit is een geldige herstelsleutel
|
||||||
|
|
||||||
# New Password
|
# New Password
|
||||||
newPassword.promptText=Voer een nieuw wachtwoord in
|
newPassword.promptText=Voer een nieuw wachtwoord in
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
## Start
|
## Start
|
||||||
## Run
|
## Run
|
||||||
## Sucess
|
## Sucess
|
||||||
|
## Missing file system capabilities
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
## General
|
## General
|
||||||
@@ -50,6 +51,7 @@
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
## Mount
|
## Mount
|
||||||
|
## Master Key
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ generic.button.back=Voltar
|
|||||||
generic.button.cancel=Cancelar
|
generic.button.cancel=Cancelar
|
||||||
generic.button.change=Alterar
|
generic.button.change=Alterar
|
||||||
generic.button.copy=Copiar
|
generic.button.copy=Copiar
|
||||||
|
generic.button.copied=Copiado!
|
||||||
generic.button.done=Pronto
|
generic.button.done=Pronto
|
||||||
generic.button.next=Próximo
|
generic.button.next=Próximo
|
||||||
generic.button.print=Imprimir
|
generic.button.print=Imprimir
|
||||||
@@ -48,12 +49,9 @@ addvaultwizard.new.generateRecoveryKeyChoice=Você não será capaz de acessar s
|
|||||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Sim, por favor, melhor seguro do que arrependido
|
addvaultwizard.new.generateRecoveryKeyChoice.yes=Sim, por favor, melhor seguro do que arrependido
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice.no=Não, obrigado, eu não vou perder minha senha
|
addvaultwizard.new.generateRecoveryKeyChoice.no=Não, obrigado, eu não vou perder minha senha
|
||||||
### Information
|
### Information
|
||||||
addvault.new.readme.storageLocation.fileName=O QUE É ESTE DIRETÓRIO.rtf
|
|
||||||
addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ ARQUIVOS DO COFRE ⚠️
|
|
||||||
addvault.new.readme.storageLocation.2=Esta é a localização do seu cofre. {\\b NÃO} altere arquivos neste diretório.
|
addvault.new.readme.storageLocation.2=Esta é a localização do seu cofre. {\\b NÃO} altere arquivos neste diretório.
|
||||||
addvault.new.readme.storageLocation.3=Se você quiser criptografar seus arquivos usando o Cryptomator, desbloqueie o cofre e use o local de armazenamento provido.
|
addvault.new.readme.storageLocation.3=Se você quiser criptografar seus arquivos usando o Cryptomator, desbloqueie o cofre e use o local de armazenamento provido.
|
||||||
addvault.new.readme.storageLocation.4=Se precisar de ajuda, tente %s.
|
addvault.new.readme.storageLocation.4=Se precisar de ajuda, tente %s.
|
||||||
addvault.new.readme.accessLocation.fileName=BEM VINDO AO SEU COFRE.rtf
|
|
||||||
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ VOLUME CRIPTOGRAFADO 🔐️
|
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ VOLUME CRIPTOGRAFADO 🔐️
|
||||||
addvault.new.readme.accessLocation.2=Este é o local de acesso do seu cofre. Qualquer arquivo que você adicionar a este volume será criptografado pelo Cryptomator. Para acessar este volume posteriormente, simplesmente desbloqueie novamente o cofre na aplicação do Cryptomator.
|
addvault.new.readme.accessLocation.2=Este é o local de acesso do seu cofre. Qualquer arquivo que você adicionar a este volume será criptografado pelo Cryptomator. Para acessar este volume posteriormente, simplesmente desbloqueie novamente o cofre na aplicação do Cryptomator.
|
||||||
addvault.new.readme.accessLocation.3=Sinta-se livre para apagar este arquivo.
|
addvault.new.readme.accessLocation.3=Sinta-se livre para apagar este arquivo.
|
||||||
@@ -103,6 +101,11 @@ migration.run.startMigrationBtn=Migrar Cofre
|
|||||||
## Sucess
|
## Sucess
|
||||||
migration.success.nextStepsInstructions="%s" migrado com sucesso.\nVocê agora pode desbloquear este cofre.
|
migration.success.nextStepsInstructions="%s" migrado com sucesso.\nVocê agora pode desbloquear este cofre.
|
||||||
migration.success.unlockNow=Desbloquear Agora
|
migration.success.unlockNow=Desbloquear Agora
|
||||||
|
## Missing file system capabilities
|
||||||
|
migration.error.missingFileSystemCapabilities.title=Sistema de arquivos não suportado
|
||||||
|
migration.error.missingFileSystemCapabilities.description=A migração não foi iniciada, porque o seu cofre está localizado em um sistema de arquivos inadequado.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=O sistema de arquivos não suporta arquivos com nome longo.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=O sistema de arquivos não suporta caminhos longos.
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
preferences.title=Preferências
|
preferences.title=Preferências
|
||||||
@@ -134,6 +137,7 @@ preferences.donationKey.getDonationKey=Obtenha uma chave de doação
|
|||||||
|
|
||||||
# Main Window
|
# Main Window
|
||||||
main.closeBtn.tooltip=Fechar
|
main.closeBtn.tooltip=Fechar
|
||||||
|
main.minimizeBtn.tooltip=Minimizar
|
||||||
main.preferencesBtn.tooltip=Preferências
|
main.preferencesBtn.tooltip=Preferências
|
||||||
main.donationKeyMissing.tooltip=Por favor, considere uma doação
|
main.donationKeyMissing.tooltip=Por favor, considere uma doação
|
||||||
## Drag 'n' Drop
|
## Drag 'n' Drop
|
||||||
@@ -170,8 +174,7 @@ wrongFileAlert.information=Você tentou adicionar um arquivo ou diretório que n
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
vaultOptions.general=Geral
|
vaultOptions.general=Geral
|
||||||
vaultOptions.general.changePasswordBtn=Alterar Senha
|
vaultOptions.general.unlockAfterStartup=Desbloqueie o cofre ao iniciar o Cryptomator
|
||||||
vaultOptions.general.showRecoveryKeyBtn=Exibir chave de recuperação
|
|
||||||
## Mount
|
## Mount
|
||||||
vaultOptions.mount=Montagem
|
vaultOptions.mount=Montagem
|
||||||
vaultOptions.mount.readonly=Somente Leitura
|
vaultOptions.mount.readonly=Somente Leitura
|
||||||
@@ -184,12 +187,20 @@ vaultOptions.mount.mountPoint.driveLetter=Usar letra de unidade atribuída
|
|||||||
vaultOptions.mount.mountPoint.custom=Caminho personalizado
|
vaultOptions.mount.mountPoint.custom=Caminho personalizado
|
||||||
vaultOptions.mount.mountPoint.directoryPickerButton=Escolher…
|
vaultOptions.mount.mountPoint.directoryPickerButton=Escolher…
|
||||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Escolha um diretório vazio
|
vaultOptions.mount.mountPoint.directoryPickerTitle=Escolha um diretório vazio
|
||||||
|
## Master Key
|
||||||
|
vaultOptions.masterkey=Senha
|
||||||
|
vaultOptions.masterkey.changePasswordBtn=Alterar Senha
|
||||||
|
vaultOptions.masterkey.recoveryKeyExpanation=Se você perder a sua senha, a única forma de restaurar acesso a um cofre é através de uma chave de recuperação.
|
||||||
|
vaultOptions.masterkey.showRecoveryKeyBtn=Exibir chave de recuperação
|
||||||
|
vaultOptions.masterkey.recoverPasswordBtn=Recuperar Senha
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
recoveryKey.title=Chave de recuperação
|
recoveryKey.title=Chave de recuperação
|
||||||
recoveryKey.enterPassword.prompt=Digite sua senha para mostrar a chave de recuperação de "%s":
|
recoveryKey.enterPassword.prompt=Digite sua senha para mostrar a chave de recuperação de "%s":
|
||||||
recoveryKey.display.message=A seguinte chave de recuperação pode ser usada para restaurar o acesso a "%s":
|
recoveryKey.display.message=A seguinte chave de recuperação pode ser usada para restaurar o acesso a "%s":
|
||||||
recoveryKey.display.StorageHints=Mantenha-a em um lugar bem seguro, por exemplo:\n • Guarde num gerenciador de senhas\n • Grave num flash drive USB\n • Imprima, ou escreva, num papel
|
recoveryKey.display.StorageHints=Mantenha-a em um lugar bem seguro, por exemplo:\n • Guarde num gerenciador de senhas\n • Grave num flash drive USB\n • Imprima, ou escreva, num papel
|
||||||
|
recoveryKey.recover.prompt=Digite sua chave de recuperação para "%s":
|
||||||
|
recoveryKey.recover.validKey=Esta é uma chave de recuperação válida
|
||||||
|
|
||||||
# New Password
|
# New Password
|
||||||
newPassword.promptText=Digite a nova senha
|
newPassword.promptText=Digite a nova senha
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ generic.button.back=Назад
|
|||||||
generic.button.cancel=Отмена
|
generic.button.cancel=Отмена
|
||||||
generic.button.change=Изменить
|
generic.button.change=Изменить
|
||||||
generic.button.copy=Копировать
|
generic.button.copy=Копировать
|
||||||
|
generic.button.copied=Скопировано!
|
||||||
generic.button.done=Готово
|
generic.button.done=Готово
|
||||||
generic.button.next=Далее
|
generic.button.next=Далее
|
||||||
generic.button.print=Распечатать
|
generic.button.print=Распечатать
|
||||||
@@ -48,12 +49,9 @@ addvaultwizard.new.generateRecoveryKeyChoice=Вы не сможете получ
|
|||||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Да, пожалуй, лучше так, чем потом сожалеть
|
addvaultwizard.new.generateRecoveryKeyChoice.yes=Да, пожалуй, лучше так, чем потом сожалеть
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice.no=Нет, спасибо, я не потеряю свой пароль
|
addvaultwizard.new.generateRecoveryKeyChoice.no=Нет, спасибо, я не потеряю свой пароль
|
||||||
### Information
|
### Information
|
||||||
addvault.new.readme.storageLocation.fileName=ЧТО ЗА КАТАЛОГ.rtf
|
|
||||||
addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ ФАЙЛЫ ХРАНИЛИЩ ⚠️
|
|
||||||
addvault.new.readme.storageLocation.2=Это место, где находится ваше хранилище. {\\b НЕ} изменяйте никакие файлы в этом каталоге.
|
addvault.new.readme.storageLocation.2=Это место, где находится ваше хранилище. {\\b НЕ} изменяйте никакие файлы в этом каталоге.
|
||||||
addvault.new.readme.storageLocation.3=Если вы хотите зашифровать файлы с помощью Cryptomator, разблокируйте хранилище и используйте предоставленный диск.
|
addvault.new.readme.storageLocation.3=Если вы хотите зашифровать файлы с помощью Cryptomator, разблокируйте хранилище и используйте предоставленный диск.
|
||||||
addvault.new.readme.storageLocation.4=Нужна помощь? Попробуйте %s.
|
addvault.new.readme.storageLocation.4=Нужна помощь? Попробуйте %s.
|
||||||
addvault.new.readme.accessLocation.fileName=ДОБРО ПОЖАЛОВАТЬ В ХРАНИЛИЩЕ.rtf
|
|
||||||
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ ЗАШИФРОВАННЫЙ ТОМ 🔐️
|
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ ЗАШИФРОВАННЫЙ ТОМ 🔐️
|
||||||
addvault.new.readme.accessLocation.2=Это место доступа в хранилище. Любые файлы, добавленные в этот том, будут зашифрованы Cryptomator. Чтобы получить доступ к этому тому позже, просто разблокируйте его из приложения Cryptomator.
|
addvault.new.readme.accessLocation.2=Это место доступа в хранилище. Любые файлы, добавленные в этот том, будут зашифрованы Cryptomator. Чтобы получить доступ к этому тому позже, просто разблокируйте его из приложения Cryptomator.
|
||||||
addvault.new.readme.accessLocation.3=Этот файл можно удалить.
|
addvault.new.readme.accessLocation.3=Этот файл можно удалить.
|
||||||
@@ -103,6 +101,11 @@ migration.run.startMigrationBtn=Перенести хранилище
|
|||||||
## Sucess
|
## Sucess
|
||||||
migration.success.nextStepsInstructions=Перенос "%s" успешно выполнен.\nТеперь можно разблокировать хранилище.
|
migration.success.nextStepsInstructions=Перенос "%s" успешно выполнен.\nТеперь можно разблокировать хранилище.
|
||||||
migration.success.unlockNow=Разблокировать
|
migration.success.unlockNow=Разблокировать
|
||||||
|
## Missing file system capabilities
|
||||||
|
migration.error.missingFileSystemCapabilities.title=Не поддерживаемая файловая система
|
||||||
|
migration.error.missingFileSystemCapabilities.description=Миграция не была запущена, так как ваше хранилище расположено в неадекватной файловой системе.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=Файловая система не поддерживает длинные имена файлов.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=Файловая система не поддерживает длинные пути.
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
preferences.title=Настройки
|
preferences.title=Настройки
|
||||||
@@ -134,6 +137,7 @@ preferences.donationKey.getDonationKey=Получить ключ пожертв
|
|||||||
|
|
||||||
# Main Window
|
# Main Window
|
||||||
main.closeBtn.tooltip=Закрыть
|
main.closeBtn.tooltip=Закрыть
|
||||||
|
main.minimizeBtn.tooltip=Минимизировать
|
||||||
main.preferencesBtn.tooltip=Настройки
|
main.preferencesBtn.tooltip=Настройки
|
||||||
main.donationKeyMissing.tooltip=Пожалуйста, обдумайте возможность поддержать приложение
|
main.donationKeyMissing.tooltip=Пожалуйста, обдумайте возможность поддержать приложение
|
||||||
## Drag 'n' Drop
|
## Drag 'n' Drop
|
||||||
@@ -170,8 +174,7 @@ wrongFileAlert.information=Вы пытались добавить файл ил
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
vaultOptions.general=Общие
|
vaultOptions.general=Общие
|
||||||
vaultOptions.general.changePasswordBtn=Изменить пароль
|
vaultOptions.general.unlockAfterStartup=Разблокировать хранилище при запуске Cryptomator
|
||||||
vaultOptions.general.showRecoveryKeyBtn=Показать ключ восстановления
|
|
||||||
## Mount
|
## Mount
|
||||||
vaultOptions.mount=Монтирование
|
vaultOptions.mount=Монтирование
|
||||||
vaultOptions.mount.readonly=Только чтение
|
vaultOptions.mount.readonly=Только чтение
|
||||||
@@ -184,12 +187,20 @@ vaultOptions.mount.mountPoint.driveLetter=Использовать назнач
|
|||||||
vaultOptions.mount.mountPoint.custom=Пользовательский путь
|
vaultOptions.mount.mountPoint.custom=Пользовательский путь
|
||||||
vaultOptions.mount.mountPoint.directoryPickerButton=Выбрать…
|
vaultOptions.mount.mountPoint.directoryPickerButton=Выбрать…
|
||||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Выберите пустой каталог
|
vaultOptions.mount.mountPoint.directoryPickerTitle=Выберите пустой каталог
|
||||||
|
## Master Key
|
||||||
|
vaultOptions.masterkey=Пароль
|
||||||
|
vaultOptions.masterkey.changePasswordBtn=Изменить пароль
|
||||||
|
vaultOptions.masterkey.recoveryKeyExpanation=Ключ восстановления - это единственный способ восстановить доступ к хранилищу в случае утери пароля.
|
||||||
|
vaultOptions.masterkey.showRecoveryKeyBtn=Показать ключ восстановления
|
||||||
|
vaultOptions.masterkey.recoverPasswordBtn=Восстановить пароль
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
recoveryKey.title=Ключ восстановления
|
recoveryKey.title=Ключ восстановления
|
||||||
recoveryKey.enterPassword.prompt=Введите пароль, чтобы показать ключ для "%s":
|
recoveryKey.enterPassword.prompt=Введите пароль, чтобы показать ключ для "%s":
|
||||||
recoveryKey.display.message=Следующий ключ восстановления можно использовать для восстановления доступа к "%s":
|
recoveryKey.display.message=Следующий ключ восстановления можно использовать для восстановления доступа к "%s":
|
||||||
recoveryKey.display.StorageHints=Храните его в надежном месте, например:\n • в менеджере паролей\n • на флэш-накопителе USB\n • распечатайте на бумаге
|
recoveryKey.display.StorageHints=Храните его в надежном месте, например:\n • в менеджере паролей\n • на флэш-накопителе USB\n • распечатайте на бумаге
|
||||||
|
recoveryKey.recover.prompt=Введите ваш ключ восстановления для "%s":
|
||||||
|
recoveryKey.recover.validKey=Это действительный ключ восстановления
|
||||||
|
|
||||||
# New Password
|
# New Password
|
||||||
newPassword.promptText=Введите новый пароль
|
newPassword.promptText=Введите новый пароль
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ generic.button.back=Bakåt
|
|||||||
generic.button.cancel=Avbryt
|
generic.button.cancel=Avbryt
|
||||||
generic.button.change=Ändra
|
generic.button.change=Ändra
|
||||||
generic.button.copy=Kopiera
|
generic.button.copy=Kopiera
|
||||||
|
generic.button.copied=Kopierad!
|
||||||
generic.button.done=Klar
|
generic.button.done=Klar
|
||||||
generic.button.next=Nästa
|
generic.button.next=Nästa
|
||||||
generic.button.print=Skriv ut
|
generic.button.print=Skriv ut
|
||||||
@@ -30,7 +31,7 @@ addvaultwizard.welcome.newButton=Skapa nytt valv
|
|||||||
addvaultwizard.welcome.existingButton=Öppna befintligt valv
|
addvaultwizard.welcome.existingButton=Öppna befintligt valv
|
||||||
## New
|
## New
|
||||||
### Name
|
### Name
|
||||||
addvaultwizard.new.nameInstruction=Välj namn för valvet
|
addvaultwizard.new.nameInstruction=Ange namn för valvet
|
||||||
addvaultwizard.new.namePrompt=Valvets namn
|
addvaultwizard.new.namePrompt=Valvets namn
|
||||||
### Location
|
### Location
|
||||||
addvaultwizard.new.locationInstruction=Var ska Cryptomator lagra de krypterade filerna för ditt valv?
|
addvaultwizard.new.locationInstruction=Var ska Cryptomator lagra de krypterade filerna för ditt valv?
|
||||||
@@ -48,12 +49,9 @@ addvaultwizard.new.generateRecoveryKeyChoice=Du kommer inte ha tillgång till di
|
|||||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Ja tack. För säkerhets skull
|
addvaultwizard.new.generateRecoveryKeyChoice.yes=Ja tack. För säkerhets skull
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice.no=Nej tack. Här slarvas inga lösenord bort
|
addvaultwizard.new.generateRecoveryKeyChoice.no=Nej tack. Här slarvas inga lösenord bort
|
||||||
### Information
|
### Information
|
||||||
addvault.new.readme.storageLocation.fileName=VAD ÄR DETTA FÖR KATALOG.rtf
|
|
||||||
addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ VALV-FILER ⚠️
|
|
||||||
addvault.new.readme.storageLocation.2=Detta är ditt valvs lagringsplats. {\\b ÄNDRA INTE} någon fil i denna katalog.
|
addvault.new.readme.storageLocation.2=Detta är ditt valvs lagringsplats. {\\b ÄNDRA INTE} någon fil i denna katalog.
|
||||||
addvault.new.readme.storageLocation.3=Om du vill kryptera filer med Cryptomator, lås upp valvet och använd angiven enhet.
|
addvault.new.readme.storageLocation.3=Om du vill kryptera filer med Cryptomator, lås upp valvet och använd angiven enhet.
|
||||||
addvault.new.readme.storageLocation.4=Om du behöver hjälp, prova %s.
|
addvault.new.readme.storageLocation.4=Om du behöver hjälp, prova %s.
|
||||||
addvault.new.readme.accessLocation.fileName=VÄLKOMMEN TILL DITT VALV.rtf
|
|
||||||
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ KRYPTERAD VOLYM 🔐️
|
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ KRYPTERAD VOLYM 🔐️
|
||||||
addvault.new.readme.accessLocation.2=Detta är ditt valvs åtkomstplats. Filer som läggs till i denna volym kommer att krypteras av Cryptomator. För att komma åt denna volym senare, lås bara upp den igen från Cryptomator-programmet.
|
addvault.new.readme.accessLocation.2=Detta är ditt valvs åtkomstplats. Filer som läggs till i denna volym kommer att krypteras av Cryptomator. För att komma åt denna volym senare, lås bara upp den igen från Cryptomator-programmet.
|
||||||
addvault.new.readme.accessLocation.3=Du kan ta bort denna fil.
|
addvault.new.readme.accessLocation.3=Du kan ta bort denna fil.
|
||||||
@@ -103,6 +101,11 @@ migration.run.startMigrationBtn=Migrera valv
|
|||||||
## Sucess
|
## Sucess
|
||||||
migration.success.nextStepsInstructions="%s" migrerades fullständigt.\nDu kan nu låsa upp ditt valv.
|
migration.success.nextStepsInstructions="%s" migrerades fullständigt.\nDu kan nu låsa upp ditt valv.
|
||||||
migration.success.unlockNow=Lås upp nu
|
migration.success.unlockNow=Lås upp nu
|
||||||
|
## Missing file system capabilities
|
||||||
|
migration.error.missingFileSystemCapabilities.title=Filsystemet stöds ej
|
||||||
|
migration.error.missingFileSystemCapabilities.description=Migreringen startades inte, eftersom ditt valv ligger på ett ogiltigt filsystem.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=Filsystemet har inte stöd för långa filnamn.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=Filsystemet har inte stöd för långa filnamn.
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
preferences.title=Inställningar
|
preferences.title=Inställningar
|
||||||
@@ -112,6 +115,9 @@ preferences.general.theme=Utseende
|
|||||||
preferences.general.startHidden=Dölj fönster när Cryptomator startar
|
preferences.general.startHidden=Dölj fönster när Cryptomator startar
|
||||||
preferences.general.debugLogging=Aktivera debug loggning
|
preferences.general.debugLogging=Aktivera debug loggning
|
||||||
preferences.general.autoStart=Starta Cryptomator vid systemstart
|
preferences.general.autoStart=Starta Cryptomator vid systemstart
|
||||||
|
preferences.general.interfaceOrientation=Gränssnittsjustering
|
||||||
|
preferences.general.interfaceOrientation.ltr=Vänster till höger
|
||||||
|
preferences.general.interfaceOrientation.rtl=Höger till vänster
|
||||||
## Volume
|
## Volume
|
||||||
preferences.volume=Virtuell enhet
|
preferences.volume=Virtuell enhet
|
||||||
preferences.volume.type=Volym-typ
|
preferences.volume.type=Volym-typ
|
||||||
@@ -131,6 +137,7 @@ preferences.donationKey.getDonationKey=Skaffa en donationsnyckel
|
|||||||
|
|
||||||
# Main Window
|
# Main Window
|
||||||
main.closeBtn.tooltip=Stäng
|
main.closeBtn.tooltip=Stäng
|
||||||
|
main.minimizeBtn.tooltip=Minimera
|
||||||
main.preferencesBtn.tooltip=Inställningar
|
main.preferencesBtn.tooltip=Inställningar
|
||||||
main.donationKeyMissing.tooltip=Överväg gärna en donation
|
main.donationKeyMissing.tooltip=Överväg gärna en donation
|
||||||
## Drag 'n' Drop
|
## Drag 'n' Drop
|
||||||
@@ -167,8 +174,7 @@ wrongFileAlert.information=Du försöker lägga till en fil eller katalog som in
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
vaultOptions.general=Allmänt
|
vaultOptions.general=Allmänt
|
||||||
vaultOptions.general.changePasswordBtn=Ändra lösenord
|
vaultOptions.general.unlockAfterStartup=Lås upp valvet när Cryptomator startas
|
||||||
vaultOptions.general.showRecoveryKeyBtn=Visa Återsällningsnyckel
|
|
||||||
## Mount
|
## Mount
|
||||||
vaultOptions.mount=Montering
|
vaultOptions.mount=Montering
|
||||||
vaultOptions.mount.readonly=Skrivskyddad
|
vaultOptions.mount.readonly=Skrivskyddad
|
||||||
@@ -181,12 +187,20 @@ vaultOptions.mount.mountPoint.driveLetter=Använd tilldelad enhetsbokstav
|
|||||||
vaultOptions.mount.mountPoint.custom=Anpassad sökväg
|
vaultOptions.mount.mountPoint.custom=Anpassad sökväg
|
||||||
vaultOptions.mount.mountPoint.directoryPickerButton=Välj…
|
vaultOptions.mount.mountPoint.directoryPickerButton=Välj…
|
||||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Välj en tom katalog
|
vaultOptions.mount.mountPoint.directoryPickerTitle=Välj en tom katalog
|
||||||
|
## Master Key
|
||||||
|
vaultOptions.masterkey=Lösenord
|
||||||
|
vaultOptions.masterkey.changePasswordBtn=Ändra lösenord
|
||||||
|
vaultOptions.masterkey.recoveryKeyExpanation=En återställningsnyckel är ditt enda sätt att återställa åtkomst till ett valv, om du förlorar ditt lösenord.
|
||||||
|
vaultOptions.masterkey.showRecoveryKeyBtn=Visa återsällningsnyckel
|
||||||
|
vaultOptions.masterkey.recoverPasswordBtn=Återställ lösenord
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
recoveryKey.title=Återställningsnyckel
|
recoveryKey.title=Återställningsnyckel
|
||||||
recoveryKey.enterPassword.prompt=Ange ditt lösenord för att visa återställningsnyckeln för "%s":
|
recoveryKey.enterPassword.prompt=Ange ditt lösenord för att visa återställningsnyckeln för "%s":
|
||||||
recoveryKey.display.message=Använd denna återställningsnyckel för att återställa åtkomst till "%s":
|
recoveryKey.display.message=Använd denna återställningsnyckel för att återställa åtkomst till "%s":
|
||||||
recoveryKey.display.StorageHints=Spara den på en säker plats, t.ex:\n • I en lösenordshanterare\n • På ett USB-minne (förvara säkert) \n • Skriv ut på ett papper (förvara säkert)
|
recoveryKey.display.StorageHints=Spara den på en säker plats, t.ex:\n • I en lösenordshanterare\n • På ett USB-minne (förvara säkert) \n • Skriv ut på ett papper (förvara säkert)
|
||||||
|
recoveryKey.recover.prompt=Ange din återställningsnyckel för "%s":
|
||||||
|
recoveryKey.recover.validKey=Detta är en giltig återställningsnyckel
|
||||||
|
|
||||||
# New Password
|
# New Password
|
||||||
newPassword.promptText=Ange ett nytt lösenord
|
newPassword.promptText=Ange ett nytt lösenord
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ generic.button.back=Geri
|
|||||||
generic.button.cancel=İptal
|
generic.button.cancel=İptal
|
||||||
generic.button.change=Değiştir
|
generic.button.change=Değiştir
|
||||||
generic.button.copy=Kopyala
|
generic.button.copy=Kopyala
|
||||||
|
generic.button.copied=Kopyalandı!
|
||||||
generic.button.done=Bitti
|
generic.button.done=Bitti
|
||||||
generic.button.next=İleri
|
generic.button.next=İleri
|
||||||
generic.button.print=Yazdır
|
generic.button.print=Yazdır
|
||||||
@@ -48,12 +49,9 @@ addvaultwizard.new.generateRecoveryKeyChoice=Şifreniz olmadan verilerinize eri
|
|||||||
addvaultwizard.new.generateRecoveryKeyChoice.yes=Evet lütfen, kafamı taşlara vurmaktan iyidir
|
addvaultwizard.new.generateRecoveryKeyChoice.yes=Evet lütfen, kafamı taşlara vurmaktan iyidir
|
||||||
addvaultwizard.new.generateRecoveryKeyChoice.no=Hayır teşekkürler, şifremi kaybetmeyeceğim
|
addvaultwizard.new.generateRecoveryKeyChoice.no=Hayır teşekkürler, şifremi kaybetmeyeceğim
|
||||||
### Information
|
### Information
|
||||||
addvault.new.readme.storageLocation.fileName=BU DİZİN NEDİR.rtf
|
|
||||||
addvault.new.readme.storageLocation.1=\\fs40\\qc ⚠️ KASA DOSYALARI ⚠️
|
|
||||||
addvault.new.readme.storageLocation.2=Bu kasanızın depolama yeri. Bu dizindeki herhangi bir dosyayı asla {\\b DEĞİŞTİRMEYİN}.
|
addvault.new.readme.storageLocation.2=Bu kasanızın depolama yeri. Bu dizindeki herhangi bir dosyayı asla {\\b DEĞİŞTİRMEYİN}.
|
||||||
addvault.new.readme.storageLocation.3=Dosyaları Cryptomator kullanarak şifrelemek istiyorsanız kasanın kilidini açın ve verilen sürücüyü kullanın.
|
addvault.new.readme.storageLocation.3=Dosyaları Cryptomator kullanarak şifrelemek istiyorsanız kasanın kilidini açın ve verilen sürücüyü kullanın.
|
||||||
addvault.new.readme.storageLocation.4=Eğer yardıma ihtiyacınız varsa, %s 'ı deneyin.
|
addvault.new.readme.storageLocation.4=Eğer yardıma ihtiyacınız varsa, %s 'ı deneyin.
|
||||||
addvault.new.readme.accessLocation.fileName=KASANIZA HOŞGELDİNİZ.rtf
|
|
||||||
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ ŞİFRELENMİŞ BİRİM 🔐️
|
addvault.new.readme.accessLocation.1=\\fs40\\qc 🔐️ ŞİFRELENMİŞ BİRİM 🔐️
|
||||||
addvault.new.readme.accessLocation.2=Bu kasanızın erişim yeri. Bu birime eklenen dosyalar Cryptomator tarafından şifrelenir. Bu birime daha sonra bir daha erişmek için Cryptomator uygulamasından tekrar kilidini kaldırmanız yeterlidir.
|
addvault.new.readme.accessLocation.2=Bu kasanızın erişim yeri. Bu birime eklenen dosyalar Cryptomator tarafından şifrelenir. Bu birime daha sonra bir daha erişmek için Cryptomator uygulamasından tekrar kilidini kaldırmanız yeterlidir.
|
||||||
addvault.new.readme.accessLocation.3=Bu dosyayı silmeye çekinmeyin.
|
addvault.new.readme.accessLocation.3=Bu dosyayı silmeye çekinmeyin.
|
||||||
@@ -103,6 +101,11 @@ migration.run.startMigrationBtn=Kasayı Taşı
|
|||||||
## Sucess
|
## Sucess
|
||||||
migration.success.nextStepsInstructions="%s" başarıyla taşındı.\nKasanızın kilidini şimdi kaldırabilirsiniz.
|
migration.success.nextStepsInstructions="%s" başarıyla taşındı.\nKasanızın kilidini şimdi kaldırabilirsiniz.
|
||||||
migration.success.unlockNow=Kilidi Şimdi Aç
|
migration.success.unlockNow=Kilidi Şimdi Aç
|
||||||
|
## Missing file system capabilities
|
||||||
|
migration.error.missingFileSystemCapabilities.title=Desteklenmeyen Dosya Sistemi
|
||||||
|
migration.error.missingFileSystemCapabilities.description=Kasanız desteklenmeyen bir dosya sisteminde bulunduğundan taşıma başlatılmadı.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_FILENAMES=Dosya sistemi, uzun belge adlarını desteklemiyor.
|
||||||
|
migration.error.missingFileSystemCapabilities.reason.LONG_PATHS=Dosya sistemi, uzun yolları desteklemiyor.
|
||||||
|
|
||||||
# Preferences
|
# Preferences
|
||||||
preferences.title=Seçenekler
|
preferences.title=Seçenekler
|
||||||
@@ -111,7 +114,7 @@ preferences.general=Genel
|
|||||||
preferences.general.theme=Görünüş ve Davranış
|
preferences.general.theme=Görünüş ve Davranış
|
||||||
preferences.general.startHidden=Cryptomator'ı başlatırken pencereyi gizle
|
preferences.general.startHidden=Cryptomator'ı başlatırken pencereyi gizle
|
||||||
preferences.general.debugLogging=Hata ayıklama günlüğünü etkinleştir
|
preferences.general.debugLogging=Hata ayıklama günlüğünü etkinleştir
|
||||||
preferences.general.autoStart=Cryptomator'u sistrm başlangıcında çalıştır
|
preferences.general.autoStart=Cryptomator'u sistem başlangıcında çalıştır
|
||||||
preferences.general.interfaceOrientation=Arayüz Yönü
|
preferences.general.interfaceOrientation=Arayüz Yönü
|
||||||
preferences.general.interfaceOrientation.ltr=Sola Yaslı
|
preferences.general.interfaceOrientation.ltr=Sola Yaslı
|
||||||
preferences.general.interfaceOrientation.rtl=Sağa Yaslı
|
preferences.general.interfaceOrientation.rtl=Sağa Yaslı
|
||||||
@@ -129,11 +132,12 @@ preferences.updates.updateAvailable=%s sürümüne güncelleme mevcut.
|
|||||||
## Donation Key
|
## Donation Key
|
||||||
preferences.donationKey=Bağış
|
preferences.donationKey=Bağış
|
||||||
preferences.donationKey.registeredFor=%s için kaydedildi
|
preferences.donationKey.registeredFor=%s için kaydedildi
|
||||||
preferences.donationKey.noDonationKey=Geçerli bir bağış anahtarı bulunamadı. Bağış anahtarı dediğimiz şey, lisans anahtarı gibidir ama ücretsiz uygulama kullanan muhteşem insanlar içindir ;-)
|
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
|
preferences.donationKey.getDonationKey=Bir bağış anahtarı al
|
||||||
|
|
||||||
# Main Window
|
# Main Window
|
||||||
main.closeBtn.tooltip=Kapat
|
main.closeBtn.tooltip=Kapat
|
||||||
|
main.minimizeBtn.tooltip=Simge Durumuna Küçült
|
||||||
main.preferencesBtn.tooltip=Seçenekler
|
main.preferencesBtn.tooltip=Seçenekler
|
||||||
main.donationKeyMissing.tooltip=Bağış yapmayı düşünmez miydiniz?
|
main.donationKeyMissing.tooltip=Bağış yapmayı düşünmez miydiniz?
|
||||||
## Drag 'n' Drop
|
## Drag 'n' Drop
|
||||||
@@ -170,10 +174,9 @@ wrongFileAlert.information=Cryptomator kasası gibi görünmeyen bir dosya veya
|
|||||||
# Vault Options
|
# Vault Options
|
||||||
## General
|
## General
|
||||||
vaultOptions.general=Genel
|
vaultOptions.general=Genel
|
||||||
vaultOptions.general.changePasswordBtn=Şifreyi Değiştir
|
vaultOptions.general.unlockAfterStartup=Cryptomator başlatıldığında kasayı aç
|
||||||
vaultOptions.general.showRecoveryKeyBtn=Kurtarma Anahtarını Göster
|
|
||||||
## Mount
|
## Mount
|
||||||
vaultOptions.mount=Bağlanılıyor
|
vaultOptions.mount=Bağlantı
|
||||||
vaultOptions.mount.readonly=Salt-Okunur
|
vaultOptions.mount.readonly=Salt-Okunur
|
||||||
vaultOptions.mount.driveName=Sürücü Adı
|
vaultOptions.mount.driveName=Sürücü Adı
|
||||||
vaultOptions.mount.customMountFlags=Özel Bağlantı Parametreleri
|
vaultOptions.mount.customMountFlags=Özel Bağlantı Parametreleri
|
||||||
@@ -184,12 +187,19 @@ vaultOptions.mount.mountPoint.driveLetter=Bir sürücü harfi kullan
|
|||||||
vaultOptions.mount.mountPoint.custom=Özel Dosya Yolu
|
vaultOptions.mount.mountPoint.custom=Özel Dosya Yolu
|
||||||
vaultOptions.mount.mountPoint.directoryPickerButton=Seç…
|
vaultOptions.mount.mountPoint.directoryPickerButton=Seç…
|
||||||
vaultOptions.mount.mountPoint.directoryPickerTitle=Boş bir dizin seçin
|
vaultOptions.mount.mountPoint.directoryPickerTitle=Boş bir dizin seçin
|
||||||
|
## Master Key
|
||||||
|
vaultOptions.masterkey=Şifre
|
||||||
|
vaultOptions.masterkey.changePasswordBtn=Şifreyi Değiştir
|
||||||
|
vaultOptions.masterkey.showRecoveryKeyBtn=Kurtarma Anahtarını Göster
|
||||||
|
vaultOptions.masterkey.recoverPasswordBtn=Parolanızı Kurtarın
|
||||||
|
|
||||||
# Recovery Key
|
# Recovery Key
|
||||||
recoveryKey.title=Kurtarma Anahtarı
|
recoveryKey.title=Kurtarma Anahtarı
|
||||||
recoveryKey.enterPassword.prompt="%s" için kurtarma anahtarını göstermek üzere şifrenizi girin:
|
recoveryKey.enterPassword.prompt="%s" için kurtarma anahtarını göstermek üzere şifrenizi girin:
|
||||||
recoveryKey.display.message=Aşağıdaki kurtarma anahtarı "%s" kasasına erişimi kazanmak için kullanılabilir:
|
recoveryKey.display.message=Aşağıdaki kurtarma anahtarı "%s" kasasına erişimi kazanmak için kullanılabilir:
|
||||||
recoveryKey.display.StorageHints=Bunu çok güvenli bir yerde saklayın, örneğin:\n • Şifre yöneticisi kullanarak depolayın\n • USB flash belleğe kaydedin\n • Bir sayfaya yazdırın
|
recoveryKey.display.StorageHints=Bunu çok güvenli bir yerde saklayın, örneğin:\n • Şifre yöneticisi kullanarak depolayın\n • USB flash belleğe kaydedin\n • Bir sayfaya yazdırın
|
||||||
|
recoveryKey.recover.prompt="%s" için kurtarma anahtarınızı girin:
|
||||||
|
recoveryKey.recover.validKey=Bu geçerli bir kurtarma anahtarı
|
||||||
|
|
||||||
# New Password
|
# New Password
|
||||||
newPassword.promptText=Yeni bir şifre girin
|
newPassword.promptText=Yeni bir şifre girin
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user