diff --git a/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java b/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java index 9ac09cb87..e9883dff4 100644 --- a/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java +++ b/main/commons/src/main/java/org/cryptomator/common/vaults/Vault.java @@ -50,9 +50,7 @@ import java.util.function.Predicate; @PerVault public class Vault { - - @Deprecated(forRemoval = true, since = "1.5.0") - public static final Predicate NOT_LOCKED = hasState(VaultState.LOCKED).negate(); + private static final Logger LOG = LoggerFactory.getLogger(Vault.class); private static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; // TODO: deduplicate constant declared in multiple classes private static final Path HOME_DIR = Paths.get(SystemUtils.USER_HOME); @@ -111,20 +109,6 @@ public class Vault { return CryptoFileSystemProvider.newFileSystem(getPath(), fsProps); } - @Deprecated(forRemoval = true, since = "1.5.0") - public void create(CharSequence passphrase) throws IOException { - if (!isValidVaultDirectory()) { - CryptoFileSystemProvider.initialize(getPath(), MASTERKEY_FILENAME, passphrase); - } else { - throw new FileAlreadyExistsException(getPath().toString()); - } - } - - @Deprecated(forRemoval = true, since = "1.5.0") - public void changePassphrase(CharSequence oldPassphrase, CharSequence newPassphrase) throws IOException, InvalidPassphraseException { - CryptoFileSystemProvider.changePassphrase(getPath(), MASTERKEY_FILENAME, oldPassphrase, newPassphrase); - } - public synchronized void unlock(CharSequence passphrase) throws CryptoException, IOException, Volume.VolumeException { if (vaultSettings.usesIndividualMountPath().get() && Strings.isNullOrEmpty(vaultSettings.individualMountPath().get())) { throw new NotDirectoryException(""); @@ -150,33 +134,9 @@ public class Vault { } } - /** - * Ejects any mounted drives and locks this vault. no-op if this vault is currently locked. - */ - @Deprecated(forRemoval = true, since = "1.5.0") - public void prepareForShutdown() { - try { - lock(false); - } catch (Volume.VolumeException e) { - if (volume.supportsForcedUnmount()) { - try { - lock(true); - } catch (Volume.VolumeException e1) { - LOG.warn("Failed to force lock vault.", e1); - } - } else { - LOG.warn("Failed to gracefully lock vault.", e); - } - } - } - public void reveal() throws Volume.VolumeException { volume.reveal(); } - - private static Predicate hasState(VaultState state) { - return vault -> vault.getState() == state; - } // ****************************************************************************** // Observable Properties @@ -283,86 +243,6 @@ public class Vault { return vaultSettings.path().getValue(); } - /** - * @deprecated use displayablePathProperty() instead - */ - @Deprecated(forRemoval = true, since = "1.5.0") - public Binding displayablePath() { - Path homeDir = Paths.get(SystemUtils.USER_HOME); - return EasyBind.map(vaultSettings.path(), p -> { - if (p.startsWith(homeDir)) { - Path relativePath = homeDir.relativize(p); - String homePrefix = SystemUtils.IS_OS_WINDOWS ? "~\\" : "~/"; - return homePrefix + relativePath.toString(); - } else { - return p.toString(); - } - }); - } - - /** - * @return Directory name without preceeding path components and file extension - * @deprecated use nameProperty() instead - */ - @Deprecated(forRemoval = true, since = "1.5.0") - public Binding name() { - return EasyBind.map(vaultSettings.path(), Path::getFileName).map(Path::toString); - } - - @Deprecated(forRemoval = true, since = "1.5.0") - public boolean doesVaultDirectoryExist() { - return Files.isDirectory(getPath()); - } - - @Deprecated(forRemoval = true, since = "1.5.0") - public boolean isValidVaultDirectory() { - return CryptoFileSystemProvider.containsVault(getPath(), MASTERKEY_FILENAME); - } - - @Deprecated(forRemoval = true, since = "1.5.0") - public long pollBytesRead() { - CryptoFileSystem fs = cryptoFileSystem.get(); - if (fs != null) { - return fs.getStats().pollBytesRead(); - } else { - return 0l; - } - } - - @Deprecated(forRemoval = true, since = "1.5.0") - public long pollBytesWritten() { - CryptoFileSystem fs = cryptoFileSystem.get(); - if (fs != null) { - return fs.getStats().pollBytesWritten(); - } else { - return 0l; - } - } - - @Deprecated(forRemoval = true, since = "1.5.0") - public String getCustomMountPath() { - return vaultSettings.individualMountPath().getValueSafe(); - } - - @Deprecated(forRemoval = true, since = "1.5.0") - public void setCustomMountPath(String mountPath) { - vaultSettings.individualMountPath().set(mountPath); - } - - @Deprecated(forRemoval = true, since = "1.5.0") - public String getMountName() { - return vaultSettings.mountName().get(); - } - - @Deprecated(forRemoval = true, since = "1.5.0") - public void setMountName(String mountName) throws IllegalArgumentException { - if (StringUtils.isBlank(mountName)) { - throw new IllegalArgumentException("mount name is empty"); - } else { - vaultSettings.mountName().set(VaultSettings.normalizeMountName(mountName)); - } - } - public boolean isHavingCustomMountFlags() { return !Strings.isNullOrEmpty(vaultSettings.mountFlags().get()); } @@ -388,25 +268,6 @@ public class Vault { vaultSettings.mountFlags().set(mountFlags); } - @Deprecated(forRemoval = true, since = "1.5.0") - public Character getWinDriveLetter() { - if (vaultSettings.winDriveLetter().get() == null) { - return null; - } else { - return vaultSettings.winDriveLetter().get().charAt(0); - } - } - - @Deprecated(forRemoval = true, since = "1.5.0") - public void setWinDriveLetter(Path root) { - if (root == null) { - vaultSettings.winDriveLetter().set(null); - } else { - char winDriveLetter = root.toString().charAt(0); - vaultSettings.winDriveLetter().set(String.valueOf(winDriveLetter)); - } - } - public String getId() { return vaultSettings.getId(); } diff --git a/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java b/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java deleted file mode 100644 index 3c1869d66..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/ExitUtil.java +++ /dev/null @@ -1,216 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016, 2017 Sebastian Stenzel and others. - * All rights reserved. - * This program and the accompanying materials are made available under the terms of the accompanying LICENSE file. - * - * Contributors: - * Sebastian Stenzel - initial API and implementation - * Jean-Noël Charon - implementation of github issue #56 - *******************************************************************************/ -package org.cryptomator.ui; - -import javafx.application.Platform; -import javafx.stage.Stage; -import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.common.settings.Settings; -import org.cryptomator.jni.JniException; -import org.cryptomator.jni.MacApplicationUiState; -import org.cryptomator.jni.MacFunctions; -import org.cryptomator.ui.fxapp.FxApplicationScoped; -import org.cryptomator.ui.l10n.Localization; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import javax.inject.Named; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineManager; -import javax.script.ScriptException; -import javax.swing.SwingUtilities; -import java.awt.AWTException; -import java.awt.Image; -import java.awt.MenuItem; -import java.awt.PopupMenu; -import java.awt.SystemTray; -import java.awt.Toolkit; -import java.awt.TrayIcon; -import java.awt.TrayIcon.MessageType; -import java.awt.event.ActionEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.IOException; -import java.util.Optional; -import java.util.concurrent.TimeUnit; - -@FxApplicationScoped -public class ExitUtil { - - private static final Logger LOG = LoggerFactory.getLogger(ExitUtil.class); - - private final Stage mainWindow; - private final Localization localization; - private final Settings settings; - private final Optional macFunctions; - private TrayIcon trayIcon; - - @Inject - public ExitUtil(@Named("mainWindow") Stage mainWindow, Localization localization, Settings settings, Optional macFunctions) { - this.mainWindow = mainWindow; - this.localization = localization; - this.settings = settings; - this.macFunctions = macFunctions; - } - - public void initExitHandler(Runnable exitCommand) { - if (SystemUtils.IS_OS_LINUX) { - initMinimizeExitHandler(exitCommand); - } else { - initTrayIconExitHandler(exitCommand); - } - } - - private void initMinimizeExitHandler(Runnable exitCommand) { - mainWindow.setOnCloseRequest(e -> { - if (Platform.isImplicitExit()) { - exitCommand.run(); - } else { - mainWindow.setIconified(true); - e.consume(); - } - }); - } - - private void initTrayIconExitHandler(Runnable exitCommand) { - trayIcon = createTrayIcon(exitCommand); - try { - // double clicking tray icon should open Cryptomator - if (SystemUtils.IS_OS_WINDOWS) { - trayIcon.addMouseListener(new TrayIconMouseListener()); - } - - SystemTray.getSystemTray().add(trayIcon); - mainWindow.setOnCloseRequest((e) -> { - if (Platform.isImplicitExit()) { - exitCommand.run(); - } else { - macFunctions.map(MacFunctions::uiState).ifPresent(JniException.ignore(MacApplicationUiState::transformToAgentApplication)); - mainWindow.close(); - this.showTrayNotification(trayIcon); - } - }); - } catch (SecurityException | AWTException ex) { - // not working? then just go ahead and close the app - mainWindow.setOnCloseRequest((ev) -> { - exitCommand.run(); - }); - } - } - - private TrayIcon createTrayIcon(Runnable exitCommand) { - final PopupMenu popup = new PopupMenu(); - - final MenuItem showItem = new MenuItem(localization.getString("tray.menu.open")); - showItem.addActionListener(this::restoreFromTray); - popup.add(showItem); - - final MenuItem exitItem = new MenuItem(localization.getString("tray.menu.quit")); - exitItem.addActionListener(e -> exitCommand.run()); - popup.add(exitItem); - - final Image image = getAppropriateTrayIconImage(true); - - return new TrayIcon(image, localization.getString("app.name"), popup); - } - - /** - * @return true if defaults read -g AppleInterfaceStyle has an exit status of 0 (i.e. _not_ returning "key not found"). - */ - private boolean isMacMenuBarDarkMode() { - try { - // check for exit status only. Once there are more modes than "dark" and "default", we might need to analyze string contents.. - final Process proc = Runtime.getRuntime().exec(new String[] {"defaults", "read", "-g", "AppleInterfaceStyle"}); - proc.waitFor(100, TimeUnit.MILLISECONDS); - return proc.exitValue() == 0; - } catch (IOException | InterruptedException | IllegalThreadStateException ex) { - // IllegalThreadStateException thrown by proc.exitValue(), if process didn't terminate - LOG.warn("Determining MAC OS X dark mode settings failed. Assuming default (light) mode."); - return false; - } - } - - private void showTrayNotification(TrayIcon trayIcon) { - int remainingTrayNotification = settings.numTrayNotifications().get(); - if (remainingTrayNotification <= 0) { - return; - } else { - settings.numTrayNotifications().set(remainingTrayNotification - 1); - } - final Runnable notificationCmd; - if (SystemUtils.IS_OS_MAC_OSX) { - final String title = localization.getString("tray.infoMsg.title"); - final String msg = localization.getString("tray.infoMsg.msg.osx"); - final String notificationCenterAppleScript = String.format("display notification \"%s\" with title \"%s\"", msg, title); - notificationCmd = () -> { - try { - final ScriptEngineManager mgr = new ScriptEngineManager(); - final ScriptEngine engine = mgr.getEngineByName("AppleScriptEngine"); - if (engine != null) { - engine.eval(notificationCenterAppleScript); - } else { - Runtime.getRuntime().exec(new String[] {"/usr/bin/osascript", "-e", notificationCenterAppleScript}); - } - } catch (ScriptException | IOException e) { - // ignore, user will notice the tray icon anyway. - } - }; - } else { - final String title = localization.getString("tray.infoMsg.title"); - final String msg = localization.getString("tray.infoMsg.msg"); - notificationCmd = () -> { - trayIcon.displayMessage(title, msg, MessageType.INFO); - }; - } - SwingUtilities.invokeLater(() -> { - notificationCmd.run(); - }); - } - - private class TrayIconMouseListener extends MouseAdapter { - - @Override - public void mouseClicked(MouseEvent e) { - if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) { - restoreFromTray(new ActionEvent(e.getSource(), e.getID(), e.paramString())); - } - } - - } - - private void restoreFromTray(ActionEvent event) { - Platform.runLater(() -> { - macFunctions.map(MacFunctions::uiState).ifPresent(JniException.ignore(MacApplicationUiState::transformToForegroundApplication)); - mainWindow.show(); - mainWindow.requestFocus(); - }); - } - - public void updateTrayIcon(boolean areAllVaultsLocked) { - if (trayIcon != null) { - Image image = getAppropriateTrayIconImage(areAllVaultsLocked); - trayIcon.setImage(image); - } - } - - private Image getAppropriateTrayIconImage(boolean areAllVaultsLocked) { - String resourceName; - if (SystemUtils.IS_OS_MAC_OSX && isMacMenuBarDarkMode()) { - resourceName = areAllVaultsLocked ? "/tray_icon_mac_white.png" : "/tray_icon_unlocked_mac_white.png"; - } else if (SystemUtils.IS_OS_MAC_OSX) { - resourceName = areAllVaultsLocked ? "/tray_icon_mac_black.png" : "/tray_icon_unlocked_mac_black.png"; - } else { - resourceName = areAllVaultsLocked ? "/tray_icon.png" : "/tray_icon_unlocked.png"; - } - return Toolkit.getDefaultToolkit().getImage(getClass().getResource(resourceName)); - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java index 0a3a77b3a..071eef96e 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/addvaultwizard/CreateNewVaultPasswordController.java @@ -27,7 +27,7 @@ import org.cryptomator.ui.common.FxmlScene; import org.cryptomator.ui.common.Tasks; import org.cryptomator.ui.controls.FontAwesome5IconView; import org.cryptomator.ui.controls.NiceSecurePasswordField; -import org.cryptomator.ui.util.PasswordStrengthUtil; +import org.cryptomator.ui.common.PasswordStrengthUtil; import org.fxmisc.easybind.EasyBind; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java index b73c0259c..30fcaa5d2 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/changepassword/ChangePasswordController.java @@ -11,11 +11,12 @@ import javafx.scene.control.Label; import javafx.scene.layout.HBox; import javafx.stage.Stage; import org.cryptomator.common.vaults.Vault; +import org.cryptomator.cryptofs.CryptoFileSystemProvider; import org.cryptomator.cryptolib.api.InvalidPassphraseException; import org.cryptomator.ui.common.FxController; import org.cryptomator.ui.controls.FontAwesome5IconView; import org.cryptomator.ui.controls.NiceSecurePasswordField; -import org.cryptomator.ui.util.PasswordStrengthUtil; +import org.cryptomator.ui.common.PasswordStrengthUtil; import org.fxmisc.easybind.EasyBind; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,6 +29,7 @@ import java.util.ResourceBundle; public class ChangePasswordController implements FxController { private static final Logger LOG = LoggerFactory.getLogger(ChangePasswordController.class); + private static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; // TODO: deduplicate constant declared in multiple classes private final Stage window; private final Vault vault; @@ -82,7 +84,7 @@ public class ChangePasswordController implements FxController { @FXML public void finish() { try { - vault.changePassphrase(oldPasswordField.getCharacters(), newPasswordField.getCharacters()); + CryptoFileSystemProvider.changePassphrase(vault.getPath(), MASTERKEY_FILENAME, oldPasswordField.getCharacters(), newPasswordField.getCharacters()); LOG.info("Successful changed password for {}", vault.getDisplayableName()); window.close(); } catch (IOException e) { diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/PasswordStrengthUtil.java b/main/ui/src/main/java/org/cryptomator/ui/common/PasswordStrengthUtil.java similarity index 63% rename from main/ui/src/main/java/org/cryptomator/ui/util/PasswordStrengthUtil.java rename to main/ui/src/main/java/org/cryptomator/ui/common/PasswordStrengthUtil.java index ac0766dd6..a9bc7e760 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/util/PasswordStrengthUtil.java +++ b/main/ui/src/main/java/org/cryptomator/ui/common/PasswordStrengthUtil.java @@ -6,15 +6,10 @@ * Contributors: * Jean-Noël Charon - initial API and implementation *******************************************************************************/ -package org.cryptomator.ui.util; +package org.cryptomator.ui.common; import com.google.common.base.Strings; import com.nulabinc.zxcvbn.Zxcvbn; -import javafx.geometry.Insets; -import javafx.scene.layout.Background; -import javafx.scene.layout.BackgroundFill; -import javafx.scene.layout.CornerRadii; -import javafx.scene.paint.Color; import org.cryptomator.ui.fxapp.FxApplicationScoped; import javax.inject.Inject; @@ -49,36 +44,6 @@ public class PasswordStrengthUtil { } } - @Deprecated - public Color getStrengthColor(Number score) { - switch (score.intValue()) { - case 0: - return Color.web("#e74c3c"); - case 1: - return Color.web("#e67e22"); - case 2: - return Color.web("#f1c40f"); - case 3: - return Color.web("#40d47e"); - case 4: - return Color.web("#27ae60"); - default: - return Color.web("#ffffff", 0.5); - } - } - - @Deprecated - public Background getBackgroundWithStrengthColor(Number score) { - Color c = this.getStrengthColor(score); - BackgroundFill fill = new BackgroundFill(c, CornerRadii.EMPTY, Insets.EMPTY); - return new Background(fill); - } - - @Deprecated - public Background getBackgroundWithStrengthColor(Number score, Number threshold) { - return score.intValue() >= threshold.intValue() ? getBackgroundWithStrengthColor(score) : getBackgroundWithStrengthColor(-1); - } - public String getStrengthDescription(Number score) { if (resourceBundle.containsKey(RESSOURCE_PREFIX + score.intValue())) { return resourceBundle.getString("passwordStrength.messageLabel." + score.intValue()); diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/ChangePasswordController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/ChangePasswordController.java deleted file mode 100644 index 43e0e5547..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/ChangePasswordController.java +++ /dev/null @@ -1,205 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016, 2017 Sebastian Stenzel and others. - * All rights reserved. - * This program and the accompanying materials are made available under the terms of the accompanying LICENSE file. - * - * Contributors: - * Sebastian Stenzel - initial API and implementation - * Jean-Noël Charon - password strength meter - *******************************************************************************/ -package org.cryptomator.ui.controllers; - -import javafx.application.Application; -import javafx.application.Platform; -import javafx.beans.Observable; -import javafx.beans.property.IntegerProperty; -import javafx.beans.property.SimpleIntegerProperty; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.Parent; -import javafx.scene.control.Button; -import javafx.scene.control.Hyperlink; -import javafx.scene.control.Label; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Region; -import javafx.scene.text.Text; -import org.cryptomator.cryptofs.CryptoFileSystemProvider; -import org.cryptomator.cryptolib.api.InvalidPassphraseException; -import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException; -import org.cryptomator.ui.controls.SecurePasswordField; -import org.cryptomator.ui.l10n.Localization; -import org.cryptomator.common.vaults.Vault; -import org.cryptomator.ui.util.PasswordStrengthUtil; -import org.fxmisc.easybind.EasyBind; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.Objects; -import java.util.Optional; - -public class ChangePasswordController implements ViewController { - - private static final Logger LOG = LoggerFactory.getLogger(ChangePasswordController.class); - private static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; // TODO: deduplicate constant declared in multiple classes - - private final Application app; - private final PasswordStrengthUtil strengthRater; - private final Localization localization; - private final IntegerProperty passwordStrength = new SimpleIntegerProperty(-1); // 0-4 - private Optional listener = Optional.empty(); - private Vault vault; - - @Inject - public ChangePasswordController(Application app, PasswordStrengthUtil strengthRater, Localization localization) { - this.app = app; - this.strengthRater = strengthRater; - this.localization = localization; - } - - @FXML - private SecurePasswordField oldPasswordField; - - @FXML - private SecurePasswordField newPasswordField; - - @FXML - private SecurePasswordField retypePasswordField; - - @FXML - private Button changePasswordButton; - - @FXML - private Text messageText; - - @FXML - private Hyperlink downloadsPageLink; - - @FXML - private Label passwordStrengthLabel; - - @FXML - private Region passwordStrengthLevel0; - - @FXML - private Region passwordStrengthLevel1; - - @FXML - private Region passwordStrengthLevel2; - - @FXML - private Region passwordStrengthLevel3; - - @FXML - private Region passwordStrengthLevel4; - - @FXML - private GridPane root; - - @Override - public void initialize() { - oldPasswordField.textProperty().addListener(this::passwordsChanged); - newPasswordField.textProperty().addListener(this::passwordsChanged); - retypePasswordField.textProperty().addListener(this::passwordsChanged); - - passwordStrengthLevel0.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(0), strengthRater::getBackgroundWithStrengthColor)); - passwordStrengthLevel1.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(1), strengthRater::getBackgroundWithStrengthColor)); - passwordStrengthLevel2.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(2), strengthRater::getBackgroundWithStrengthColor)); - passwordStrengthLevel3.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(3), strengthRater::getBackgroundWithStrengthColor)); - passwordStrengthLevel4.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(4), strengthRater::getBackgroundWithStrengthColor)); - passwordStrengthLabel.textProperty().bind(EasyBind.map(passwordStrength, strengthRater::getStrengthDescription)); - } - - private void passwordsChanged(@SuppressWarnings("unused") Observable observable) { - boolean oldPasswordEmpty = oldPasswordField.getCharacters().length() == 0; - boolean newPasswordEmpty = newPasswordField.getCharacters().length() == 0; - boolean passwordsEqual = newPasswordField.getCharacters().equals(retypePasswordField.getCharacters()); - changePasswordButton.setDisable(oldPasswordEmpty || newPasswordEmpty || !passwordsEqual); - passwordStrength.set(strengthRater.computeRate(newPasswordField.getCharacters().toString())); - } - - @Override - public Parent getRoot() { - return root; - } - - @Override - public void focus() { - oldPasswordField.requestFocus(); - } - - void setVault(Vault vault) { - this.vault = Objects.requireNonNull(vault); - // trigger "default" change to refresh key bindings: - changePasswordButton.setDefaultButton(false); - changePasswordButton.setDefaultButton(true); - messageText.setText(null); - } - - // **************************************** - // Downloads link - // **************************************** - - @FXML - public void didClickDownloadsLink(ActionEvent event) { - app.getHostServices().showDocument("https://cryptomator.org/downloads/"); - } - - // **************************************** - // Change password button - // **************************************** - - @FXML - private void didClickChangePasswordButton(ActionEvent event) { - downloadsPageLink.setVisible(false); - try { - CryptoFileSystemProvider.changePassphrase(vault.getPath(), MASTERKEY_FILENAME, oldPasswordField.getCharacters(), newPasswordField.getCharacters()); - messageText.setText(null); - listener.ifPresent(this::invokeListenerLater); - } catch (InvalidPassphraseException e) { - messageText.setText(localization.getString("changePassword.errorMessage.wrongPassword")); - Platform.runLater(oldPasswordField::requestFocus); - } catch (UncheckedIOException | IOException ex) { - messageText.setText(localization.getString("changePassword.errorMessage.decryptionFailed")); - LOG.error("Decryption failed for technical reasons.", ex); - } catch (UnsupportedVaultFormatException e) { - downloadsPageLink.setVisible(true); - LOG.warn("Unable to unlock vault: " + e.getMessage()); - if (e.isVaultOlderThanSoftware()) { - messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " "); - } else if (e.isSoftwareOlderThanVault()) { - messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " "); - } - } finally { - oldPasswordField.swipe(); - newPasswordField.swipe(); - retypePasswordField.swipe(); - } - } - - /* Getter/Setter */ - - public ChangePasswordListener getListener() { - return listener.orElse(null); - } - - public void setListener(ChangePasswordListener listener) { - this.listener = Optional.ofNullable(listener); - } - - /* callback */ - - private void invokeListenerLater(ChangePasswordListener listener) { - Platform.runLater(() -> { - listener.didChangePassword(); - }); - } - - @FunctionalInterface - interface ChangePasswordListener { - void didChangePassword(); - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/InitializeController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/InitializeController.java deleted file mode 100644 index ee8076eca..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/InitializeController.java +++ /dev/null @@ -1,167 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014, 2017 Sebastian Stenzel - * All rights reserved. - * This program and the accompanying materials are made available under the terms of the accompanying LICENSE file. - * - * Contributors: - * Sebastian Stenzel - initial API and implementation - * Jean-Noël Charon - password strength meter - ******************************************************************************/ -package org.cryptomator.ui.controllers; - -import javafx.application.Platform; -import javafx.beans.Observable; -import javafx.beans.property.IntegerProperty; -import javafx.beans.property.SimpleIntegerProperty; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.Parent; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Region; -import org.cryptomator.ui.controls.SecurePasswordField; -import org.cryptomator.ui.l10n.Localization; -import org.cryptomator.common.vaults.Vault; -import org.cryptomator.ui.util.PasswordStrengthUtil; -import org.fxmisc.easybind.EasyBind; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.io.IOException; -import java.nio.file.FileAlreadyExistsException; -import java.util.Objects; -import java.util.Optional; - -public class InitializeController implements ViewController { - - private static final Logger LOG = LoggerFactory.getLogger(InitializeController.class); - - private final Localization localization; - private final PasswordStrengthUtil strengthRater; - private IntegerProperty passwordStrength = new SimpleIntegerProperty(-1); // strengths: 0-4 - private Optional listener = Optional.empty(); - private Vault vault; - - @Inject - public InitializeController(Localization localization, PasswordStrengthUtil strengthRater) { - this.localization = localization; - this.strengthRater = strengthRater; - } - - @FXML - private SecurePasswordField passwordField; - - @FXML - private SecurePasswordField retypePasswordField; - - @FXML - private Button okButton; - - @FXML - private Label messageLabel; - - @FXML - private Label passwordStrengthLabel; - - @FXML - private Region passwordStrengthLevel0; - - @FXML - private Region passwordStrengthLevel1; - - @FXML - private Region passwordStrengthLevel2; - - @FXML - private Region passwordStrengthLevel3; - - @FXML - private Region passwordStrengthLevel4; - - @FXML - private GridPane root; - - @Override - public void initialize() { - passwordField.textProperty().addListener(this::passwordsChanged); - retypePasswordField.textProperty().addListener(this::passwordsChanged); - - passwordStrengthLevel0.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(0), strengthRater::getBackgroundWithStrengthColor)); - passwordStrengthLevel1.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(1), strengthRater::getBackgroundWithStrengthColor)); - passwordStrengthLevel2.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(2), strengthRater::getBackgroundWithStrengthColor)); - passwordStrengthLevel3.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(3), strengthRater::getBackgroundWithStrengthColor)); - passwordStrengthLevel4.backgroundProperty().bind(EasyBind.combine(passwordStrength, new SimpleIntegerProperty(4), strengthRater::getBackgroundWithStrengthColor)); - passwordStrengthLabel.textProperty().bind(EasyBind.map(passwordStrength, strengthRater::getStrengthDescription)); - } - - private void passwordsChanged(@SuppressWarnings("unused") Observable observable) { - boolean passwordsEmpty = passwordField.getCharacters().length() == 0; - boolean passwordsEqual = passwordField.getCharacters().equals(retypePasswordField.getCharacters()); - okButton.setDisable(passwordsEmpty || !passwordsEqual); - passwordStrength.set(strengthRater.computeRate(passwordField.getCharacters().toString())); - } - - @Override - public Parent getRoot() { - return root; - } - - @Override - public void focus() { - passwordField.requestFocus(); - } - - void setVault(Vault vault) { - this.vault = Objects.requireNonNull(vault); - // trigger "default" change to refresh key bindings: - okButton.setDefaultButton(false); - okButton.setDefaultButton(true); - } - - // **************************************** - // OK button - // **************************************** - - @FXML - protected void initializeVault(ActionEvent event) { - final CharSequence passphrase = passwordField.getCharacters(); - try { - vault.create(passphrase); - listener.ifPresent(this::invokeListenerLater); - } catch (FileAlreadyExistsException ex) { - messageLabel.setText(localization.getString("initialize.messageLabel.alreadyInitialized")); - } catch (IOException ex) { - LOG.error("I/O Exception", ex); - messageLabel.setText(localization.getString("initialize.messageLabel.initializationFailed")); - } finally { - passwordField.swipe(); - retypePasswordField.swipe(); - } - } - - /* Getter/Setter */ - - public InitializationListener getListener() { - return listener.orElse(null); - } - - public void setListener(InitializationListener listener) { - this.listener = Optional.ofNullable(listener); - } - - /* callback */ - - private void invokeListenerLater(InitializationListener listener) { - Platform.runLater(() -> { - listener.didInitialize(); - }); - } - - @FunctionalInterface - interface InitializationListener { - void didInitialize(); - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java deleted file mode 100644 index 1430127ae..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/MainController.java +++ /dev/null @@ -1,557 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014, 2017 Sebastian Stenzel - * All rights reserved. - * This program and the accompanying materials are made available under the terms of the accompanying LICENSE file. - * - * Contributors: - * Sebastian Stenzel - initial API and implementation - * Jean-Noël Charon - confirmation dialog on vault removal - ******************************************************************************/ -package org.cryptomator.ui.controllers; - -import javafx.application.Application; -import javafx.application.Platform; -import javafx.beans.binding.Binding; -import javafx.beans.binding.Bindings; -import javafx.beans.binding.BooleanBinding; -import javafx.beans.binding.BooleanExpression; -import javafx.beans.binding.ObjectExpression; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.geometry.Side; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.scene.control.Alert; -import javafx.scene.control.Button; -import javafx.scene.control.ButtonType; -import javafx.scene.control.Cell; -import javafx.scene.control.ContextMenu; -import javafx.scene.control.ListCell; -import javafx.scene.control.ListView; -import javafx.scene.control.MenuItem; -import javafx.scene.control.ToggleButton; -import javafx.scene.image.Image; -import javafx.scene.input.KeyCode; -import javafx.scene.input.KeyEvent; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; -import javafx.scene.text.Font; -import javafx.stage.FileChooser; -import javafx.stage.Stage; -import javafx.util.Duration; -import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.common.vaults.VaultState; -import org.cryptomator.ui.fxapp.FxApplicationScoped; -import org.cryptomator.ui.ExitUtil; -import org.cryptomator.ui.controls.DirectoryListCell; -import org.cryptomator.ui.l10n.Localization; -import org.cryptomator.ui.launcher.AppLaunchEvent; -import org.cryptomator.ui.model.AutoUnlocker; -import org.cryptomator.common.vaults.Vault; -import org.cryptomator.common.vaults.VaultListManager; -import org.cryptomator.ui.model.upgrade.UpgradeStrategies; -import org.cryptomator.ui.model.upgrade.UpgradeStrategy; -import org.cryptomator.ui.util.DialogBuilderUtil; -import org.cryptomator.ui.common.Tasks; -import org.fxmisc.easybind.EasyBind; -import org.fxmisc.easybind.Subscription; -import org.fxmisc.easybind.monadic.MonadicBinding; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import javax.inject.Named; -import java.awt.Desktop; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; -import java.nio.file.Files; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutorService; -import java.util.stream.Stream; - -import static org.cryptomator.ui.util.DialogBuilderUtil.buildErrorDialog; - -@FxApplicationScoped -public class MainController implements ViewController { - - private static final Logger LOG = LoggerFactory.getLogger(MainController.class); - private static final String ACTIVE_WINDOW_STYLE_CLASS = "active-window"; - private static final String INACTIVE_WINDOW_STYLE_CLASS = "inactive-window"; - - private final Stage mainWindow; - private final ExitUtil exitUtil; - private final Localization localization; - private final ExecutorService executorService; - private final BlockingQueue launchEventQueue; - private final VaultListManager vaultFactoy; - private final ViewControllerLoader viewControllerLoader; - private final ObjectProperty activeController = new SimpleObjectProperty<>(); - private final ObservableList vaults; - private final BooleanBinding areAllVaultsLocked; - private final ObjectProperty selectedVault = new SimpleObjectProperty<>(); - private final ObjectExpression selectedVaultState = ObjectExpression.objectExpression(EasyBind.select(selectedVault).selectObject(Vault::stateProperty)); - private final BooleanExpression isSelectedVaultValid = BooleanExpression.booleanExpression(EasyBind.monadic(selectedVault).map(Vault::isValidVaultDirectory).orElse(false)); - private final BooleanExpression canEditSelectedVault = selectedVaultState.isEqualTo(VaultState.LOCKED); - private final MonadicBinding upgradeStrategyForSelectedVault; - private final BooleanBinding isShowingSettings; - private final Map unlockedVaults = new HashMap<>(); - - private Subscription subs = Subscription.EMPTY; - - @Inject - public MainController(@Named("mainWindow") Stage mainWindow, ExecutorService executorService, @Named("launchEventQueue") BlockingQueue launchEventQueue, ExitUtil exitUtil, Localization localization, - VaultListManager vaultFactoy, ViewControllerLoader viewControllerLoader, UpgradeStrategies upgradeStrategies, ObservableList vaults, AutoUnlocker autoUnlocker) { - this.mainWindow = mainWindow; - this.executorService = executorService; - this.launchEventQueue = launchEventQueue; - this.exitUtil = exitUtil; - this.localization = localization; - this.vaultFactoy = vaultFactoy; - this.viewControllerLoader = viewControllerLoader; - this.vaults = vaults; - - // derived bindings: - this.isShowingSettings = Bindings.equal(SettingsController.class, EasyBind.monadic(activeController).map(ViewController::getClass)); - this.upgradeStrategyForSelectedVault = EasyBind.monadic(selectedVault).map(upgradeStrategies::getUpgradeStrategy); - this.areAllVaultsLocked = Bindings.isEmpty(FXCollections.observableList(vaults, Vault::observables).filtered(Vault.NOT_LOCKED)); - - EasyBind.subscribe(areAllVaultsLocked, exitUtil::updateTrayIcon); - EasyBind.subscribe(areAllVaultsLocked, Platform::setImplicitExit); - autoUnlocker.unlockAllSilently(); - - try { - Desktop.getDesktop().setPreferencesHandler(e -> { - Platform.runLater(this::toggleShowSettings); - }); - } catch (UnsupportedOperationException e) { - LOG.info("Unable to setPreferencesHandler, probably not supported on this OS."); - } - } - - @FXML - private ContextMenu vaultListCellContextMenu; - - @FXML - private MenuItem changePasswordMenuItem; - - @FXML - private ContextMenu addVaultContextMenu; - - @FXML - private HBox root; - - @FXML - private ListView vaultList; - - @FXML - private ToggleButton addVaultButton; - - @FXML - private Button removeVaultButton; - - @FXML - private ToggleButton settingsButton; - - @FXML - private Pane contentPane; - - @FXML - private Pane emptyListInstructions; - - @Override - public void initialize() { - vaultList.setItems(vaults); - vaultList.getSelectionModel().clearSelection(); - vaultList.setOnKeyReleased(this::didPressKeyOnList); - vaultList.setCellFactory(this::createDirecoryListCell); - root.setOnKeyReleased(this::didPressKeyOnRoot); - activeController.set(viewControllerLoader.load("/fxml/welcome.fxml")); - selectedVault.bind(vaultList.getSelectionModel().selectedItemProperty()); - removeVaultButton.disableProperty().bind(canEditSelectedVault.not()); - emptyListInstructions.visibleProperty().bind(Bindings.isEmpty(vaults)); - changePasswordMenuItem.visibleProperty().bind(isSelectedVaultValid.and(Bindings.isNull(upgradeStrategyForSelectedVault))); - - subs = subs.and(EasyBind.subscribe(selectedVault, this::selectedVaultDidChange)); - subs = subs.and(EasyBind.subscribe(activeController, this::activeControllerDidChange)); - subs = subs.and(EasyBind.subscribe(isShowingSettings, settingsButton::setSelected)); - subs = subs.and(EasyBind.subscribe(addVaultContextMenu.showingProperty(), addVaultButton::setSelected)); - } - - @Override - public Parent getRoot() { - return root; - } - - public void initStage(Stage stage) { - stage.setScene(new Scene(getRoot())); - stage.sizeToScene(); - stage.setTitle(localization.getString("app.name")); // set once before bind to avoid display bugs with Linux window managers - stage.titleProperty().bind(windowTitle()); - stage.setResizable(false); - loadFont("/css/ionicons.ttf"); - loadFont("/css/fontawesome-webfont.ttf"); - if (SystemUtils.IS_OS_MAC_OSX) { - subs = subs.and(EasyBind.includeWhen(mainWindow.getScene().getRoot().getStyleClass(), ACTIVE_WINDOW_STYLE_CLASS, mainWindow.focusedProperty())); - subs = subs.and(EasyBind.includeWhen(mainWindow.getScene().getRoot().getStyleClass(), INACTIVE_WINDOW_STYLE_CLASS, mainWindow.focusedProperty().not())); - Application.setUserAgentStylesheet(getClass().getResource("/css/mac_theme.css").toString()); - } else if (SystemUtils.IS_OS_LINUX) { - stage.getIcons().add(new Image(getClass().getResourceAsStream("/window_icon_512.png"))); - Application.setUserAgentStylesheet(getClass().getResource("/css/linux_theme.css").toString()); - } else if (SystemUtils.IS_OS_WINDOWS) { - stage.getIcons().add(new Image(getClass().getResourceAsStream("/window_icon_32.png"))); - Application.setUserAgentStylesheet(getClass().getResource("/css/win_theme.css").toString()); - } - exitUtil.initExitHandler(() -> Platform.runLater(this::gracefulShutdown)); - listenToFileOpenRequests(stage); - } - - private void gracefulShutdown() { - vaults.filtered(Vault.NOT_LOCKED).forEach(Vault::prepareForShutdown); - if (!vaults.filtered(Vault.NOT_LOCKED).isEmpty()) { - mainWindow.show(); // to keep the application open - ButtonType tryAgainButtonType = new ButtonType(localization.getString("main.gracefulShutdown.button.tryAgain")); - ButtonType forceShutdownButtonType = new ButtonType(localization.getString("main.gracefulShutdown.button.forceShutdown")); - Alert gracefulShutdownDialog = DialogBuilderUtil.buildGracefulShutdownDialog( - localization.getString("main.gracefulShutdown.dialog.title"), localization.getString("main.gracefulShutdown.dialog.header"), localization.getString("main.gracefulShutdown.dialog.content"), - forceShutdownButtonType, ButtonType.CANCEL, forceShutdownButtonType, tryAgainButtonType); - Optional choice = gracefulShutdownDialog.showAndWait(); - choice.ifPresent(btnType -> { - if (tryAgainButtonType.equals(btnType)) { - gracefulShutdown(); - } else if (forceShutdownButtonType.equals(btnType)) { - Platform.runLater(Platform::exit); - } else { - if (!vaults.filtered(Vault.NOT_LOCKED).isEmpty()) { - showUnlockedView(vaults.get(0), false); //if there are still unlocked vaults, show one of them - } else { - showUnlockView(UnlockController.State.UNLOCKING); //otherwise show any vault - } - } - }); - } else { - Platform.runLater(Platform::exit); - } - } - - private void loadFont(String resourcePath) { - try (InputStream in = getClass().getResourceAsStream(resourcePath)) { - Font.loadFont(in, 12.0); - } catch (IOException e) { - LOG.warn("Error loading font from path: " + resourcePath, e); - } - } - - private void listenToFileOpenRequests(Stage stage) { - Tasks.create(launchEventQueue::take).onSuccess(event -> { - stage.setIconified(false); - stage.show(); - stage.toFront(); - stage.requestFocus(); - event.getPathsToOpen().forEach(path -> addVault(path, true)); - }).schedulePeriodically(executorService, Duration.ZERO, Duration.ZERO); - } - - private ListCell createDirecoryListCell(ListView param) { - final DirectoryListCell cell = new DirectoryListCell(); - cell.setVaultContextMenu(vaultListCellContextMenu); - cell.setOnMouseClicked(this::didClickOnListCell); - return cell; - } - - // **************************************** - // UI Events - // **************************************** - - @FXML - private void didClickAddVault() { - if (addVaultContextMenu.isShowing()) { - addVaultContextMenu.hide(); - } else { - addVaultContextMenu.show(addVaultButton, Side.BOTTOM, 0.0, 0.0); - } - } - - @FXML - private void didClickCreateNewVault() { - final FileChooser fileChooser = new FileChooser(); - final File file = fileChooser.showSaveDialog(mainWindow); - if (file == null) { - return; - } - try { - final Path vaultDir = file.toPath(); - if (Files.exists(vaultDir)) { - try (Stream stream = Files.list(vaultDir)) { - if (stream.filter(this::isNotHidden).findAny().isPresent()) { - buildErrorDialog( // - localization.getString("main.createVault.nonEmptyDir.title"), // - localization.getString("main.createVault.nonEmptyDir.header"), // - localization.getString("main.createVault.nonEmptyDir.content"), // - ButtonType.OK).show(); - return; - } - } - } else { - Files.createDirectory(vaultDir); - } - addVault(vaultDir, true); - } catch (IOException e) { - LOG.error("Unable to create vault", e); - } - } - - private boolean isNotHidden(Path file) { - return !file.getFileName().toString().startsWith("."); - } - - @FXML - private void didClickAddExistingVaults() { - final FileChooser fileChooser = new FileChooser(); - fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("Cryptomator Masterkey", "*.cryptomator")); - final List files = fileChooser.showOpenMultipleDialog(mainWindow); - if (files != null) { - for (final File file : files) { - addVault(file.toPath(), true); - } - } - } - - /** - * adds the given directory or selects it if it is already in the list of directories. - * - * @param path to a vault directory or masterkey file - */ - public void addVault(final Path path, boolean select) { - final Path vaultPath; - if (path != null && Files.isDirectory(path)) { - vaultPath = path; - } else if (path != null && Files.isReadable(path)) { - vaultPath = path.getParent(); - } else { - LOG.warn("Ignoring attempt to add vault with invalid path: {}", path); - return; - } - - final Vault vault = vaults.stream().filter(v -> v.getPath().equals(vaultPath)).findAny().orElseGet(() -> { - try { - return vaultFactoy.add(vaultPath); - } catch (NoSuchFileException e) { - throw new UncheckedIOException(e); - } - }); - - if (!vaults.contains(vault)) { - vaults.add(vault); - } - if (select) { - vaultList.getSelectionModel().select(vault); - activeController.get().focus(); - } - } - - @FXML - private void didClickRemoveSelectedEntry() { - Alert confirmDialog = DialogBuilderUtil.buildConfirmationDialog( // - localization.getString("main.directoryList.remove.confirmation.title"), // - localization.getString("main.directoryList.remove.confirmation.header"), // - localization.getString("main.directoryList.remove.confirmation.content"), // - SystemUtils.IS_OS_MAC_OSX ? ButtonType.CANCEL : ButtonType.OK); - - Optional choice = confirmDialog.showAndWait(); - if (ButtonType.OK.equals(choice.get())) { - vaults.remove(selectedVault.get()); - if (vaults.isEmpty()) { - activeController.set(viewControllerLoader.load("/fxml/welcome.fxml")); - } else { - activeController.get().focus(); - } - } - } - - @FXML - private void didClickChangePassword() { - showChangePasswordView(); - } - - @FXML - private void didClickShowSettings() { - toggleShowSettings(); - } - - private void toggleShowSettings() { - if (isShowingSettings.get()) { - showWelcomeView(); - } else { - showPreferencesView(); - } - vaultList.getSelectionModel().clearSelection(); - } - - // **************************************** - // Binding Listeners - // **************************************** - - private void activeControllerDidChange(ViewController newValue) { - final Parent root = newValue.getRoot(); - contentPane.getChildren().clear(); - contentPane.getChildren().add(root); - } - - private void selectedVaultDidChange(Vault newValue) { - if (newValue == null) { - return; - } - if (newValue.getState() != VaultState.LOCKED) { - this.showUnlockedView(newValue, false); - } else if (!newValue.doesVaultDirectoryExist()) { - this.showNotFoundView(); - } else if (newValue.isValidVaultDirectory() && upgradeStrategyForSelectedVault.isPresent()) { - this.showUpgradeView(); - } else if (newValue.isValidVaultDirectory()) { - this.showUnlockView(UnlockController.State.UNLOCKING); - } else { - this.showInitializeView(); - } - } - - private void didPressKeyOnList(KeyEvent e) { - if (e.getCode() == KeyCode.ENTER || e.getCode() == KeyCode.SPACE) { - activeController.get().focus(); - } - } - - private void didPressKeyOnRoot(KeyEvent event) { - boolean triggered; - if (SystemUtils.IS_OS_MAC) { - triggered = event.isMetaDown(); - } else { - triggered = event.isControlDown() && !event.isAltDown(); - } - if (triggered && event.getCode().isDigitKey()) { - int digit = Integer.valueOf(event.getText()); - switch (digit) { - case 0: { - vaultList.getSelectionModel().clearSelection(); - showWelcomeView(); - return; - } - default: { - vaultList.getSelectionModel().select(digit - 1); - activeController.get().focus(); - return; - } - } - } - } - - private void didClickOnListCell(MouseEvent e) { - if (MouseEvent.MOUSE_CLICKED.equals(e.getEventType()) && e.getSource() instanceof Cell && ((Cell) e.getSource()).isSelected()) { - activeController.get().focus(); - } - } - - // **************************************** - // Public Bindings - // **************************************** - - public Binding windowTitle() { - return EasyBind.monadic(selectedVault).flatMap(Vault::name).orElse(localization.getString("app.name")); - } - - // **************************************** - // Subcontroller for right panel - // **************************************** - - private void showWelcomeView() { - activeController.set(viewControllerLoader.load("/fxml/welcome.fxml")); - } - - private void showPreferencesView() { - activeController.set(viewControllerLoader.load("/fxml/settings.fxml")); - } - - private void showNotFoundView() { - activeController.set(viewControllerLoader.load("/fxml/notfound.fxml")); - } - - private void showInitializeView() { - final InitializeController ctrl = viewControllerLoader.load("/fxml/initialize.fxml"); - ctrl.setVault(selectedVault.get()); - ctrl.setListener(this::didInitialize); - activeController.set(ctrl); - } - - public void didInitialize() { - showUnlockView(UnlockController.State.INITIALIZED); - activeController.get().focus(); - } - - private void showUpgradeView() { - final UpgradeController ctrl = viewControllerLoader.load("/fxml/upgrade.fxml"); - ctrl.setVault(selectedVault.get()); - ctrl.setListener(this::didUpgrade); - activeController.set(ctrl); - } - - public void didUpgrade() { - showUnlockView(UnlockController.State.UPGRADED); - activeController.get().focus(); - } - - private void showUnlockView(UnlockController.State state) { - final UnlockController ctrl = viewControllerLoader.load("/fxml/unlock.fxml"); - ctrl.setVault(selectedVault.get(), state); - ctrl.setListener(this::didUnlock); - activeController.set(ctrl); - } - - public void didUnlock(Vault vault) { - if (vault.equals(selectedVault.getValue())) { - this.showUnlockedView(vault, vault.getVaultSettings().revealAfterMount().getValue()); - } - } - - private void showUnlockedView(Vault vault, boolean reveal) { - final UnlockedController ctrl = unlockedVaults.computeIfAbsent(vault, k -> viewControllerLoader.load("/fxml/unlocked.fxml")); - ctrl.setVault(vault); - ctrl.setListener(this::didLock); - if (reveal) { - ctrl.revealVault(vault); - } - activeController.set(ctrl); - } - - public void didLock(UnlockedController ctrl) { - unlockedVaults.remove(ctrl.getVault()); - if (ctrl.getVault().getId() == selectedVault.get().getId()) { - showUnlockView(UnlockController.State.UNLOCKING); - } - activeController.get().focus(); - } - - private void showChangePasswordView() { - final ChangePasswordController ctrl = viewControllerLoader.load("/fxml/change_password.fxml"); - ctrl.setVault(selectedVault.get()); - ctrl.setListener(this::didChangePassword); - activeController.set(ctrl); - Platform.runLater(ctrl::focus); - } - - public void didChangePassword() { - showUnlockView(UnlockController.State.PASSWORD_CHANGED); - activeController.get().focus(); - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java deleted file mode 100644 index 94ae1d220..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/NotFoundController.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.ui.controllers; - -import javafx.fxml.FXML; -import javafx.scene.Parent; -import javafx.scene.layout.VBox; -import org.cryptomator.ui.fxapp.FxApplicationScoped; - -import javax.inject.Inject; - -@FxApplicationScoped -public class NotFoundController implements ViewController { - - @Inject - public NotFoundController() { - // no-op - } - - @FXML - VBox root; - - @Override - public Parent getRoot() { - return root; - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java deleted file mode 100644 index f609a3c98..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/SettingsController.java +++ /dev/null @@ -1,180 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014, 2017 Sebastian Stenzel - * All rights reserved. - * This program and the accompanying materials are made available under the terms of the accompanying LICENSE file. - * - * Contributors: - * Sebastian Stenzel - initial API and implementation - ******************************************************************************/ -package org.cryptomator.ui.controllers; - -import com.google.common.base.CharMatcher; -import com.google.common.base.Strings; -import javafx.beans.Observable; -import javafx.beans.binding.Bindings; -import javafx.fxml.FXML; -import javafx.scene.Group; -import javafx.scene.Parent; -import javafx.scene.control.Button; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ChoiceBox; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.input.KeyEvent; -import javafx.scene.layout.VBox; -import javafx.util.StringConverter; -import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.ui.fxapp.FxApplicationScoped; -import org.cryptomator.common.settings.Settings; -import org.cryptomator.common.settings.VolumeImpl; -import org.cryptomator.ui.l10n.Localization; -import org.cryptomator.common.vaults.Volume; - -import javax.inject.Inject; -import javax.inject.Named; -import java.util.Optional; - -@FxApplicationScoped -public class SettingsController implements ViewController { - - private static final CharMatcher DIGITS_MATCHER = CharMatcher.inRange('0', '9'); - - private final Localization localization; - private final Settings settings; - private final Optional applicationVersion; - - @Inject - public SettingsController(Localization localization, Settings settings, @Named("applicationVersion") Optional applicationVersion) { - this.localization = localization; - this.settings = settings; - this.applicationVersion = applicationVersion; - this.webdavSettings = new Group(); - } - - @FXML - private CheckBox checkForUpdatesCheckbox; - - private Group webdavSettings; - - @FXML - private Label portFieldLabel; - - @FXML - private TextField portField; - - @FXML - private Button changePortButton; - - @FXML - private Label versionLabel; - - @FXML - private Label prefGvfsSchemeLabel; - - @FXML - private ChoiceBox prefGvfsScheme; - - @FXML - private ChoiceBox volume; - - @FXML - private CheckBox debugModeCheckbox; - - @FXML - private VBox root; - - @Override - public void initialize() { - versionLabel.setText(String.format(localization.getString("settings.version.label"), applicationVersion.orElse("SNAPSHOT"))); - checkForUpdatesCheckbox.setDisable(areUpdatesManagedExternally()); - checkForUpdatesCheckbox.setSelected(settings.checkForUpdates().get() && !areUpdatesManagedExternally()); - - //NIOADAPTER - volume.getItems().addAll(Volume.getCurrentSupportedAdapters()); - volume.setValue(settings.preferredVolumeImpl().get()); - volume.setConverter(new NioAdapterImplStringConverter()); - volume.valueProperty().addListener(this::setVisibilityGvfsElements); - - //WEBDAV - webdavSettings.visibleProperty().bind(volume.valueProperty().isEqualTo(VolumeImpl.WEBDAV)); - webdavSettings.managedProperty().bind(webdavSettings.visibleProperty()); - prefGvfsScheme.managedProperty().bind(webdavSettings.visibleProperty()); - prefGvfsSchemeLabel.managedProperty().bind(webdavSettings.visibleProperty()); - portFieldLabel.managedProperty().bind(webdavSettings.visibleProperty()); - portFieldLabel.visibleProperty().bind(webdavSettings.visibleProperty()); - changePortButton.managedProperty().bind(webdavSettings.visibleProperty()); - portField.managedProperty().bind(webdavSettings.visibleProperty()); - portField.visibleProperty().bind(webdavSettings.visibleProperty()); - portField.setText(String.valueOf(settings.port().intValue())); - portField.addEventFilter(KeyEvent.KEY_TYPED, this::filterNumericKeyEvents); - changePortButton.visibleProperty().bind(settings.port().asString().isNotEqualTo(portField.textProperty())); - changePortButton.disableProperty().bind(Bindings.createBooleanBinding(this::isPortValid, portField.textProperty()).not()); - prefGvfsScheme.getItems().add("dav"); - prefGvfsScheme.getItems().add("webdav"); - // prefGvfsScheme.setValue(settings.preferredGvfsScheme().get()); - prefGvfsSchemeLabel.setVisible(SystemUtils.IS_OS_LINUX); - prefGvfsScheme.setVisible(SystemUtils.IS_OS_LINUX); - - debugModeCheckbox.setSelected(settings.debugMode().get()); - - settings.checkForUpdates().bind(checkForUpdatesCheckbox.selectedProperty()); - // settings.preferredGvfsScheme().bind(prefGvfsScheme.valueProperty()); - settings.preferredVolumeImpl().bind(volume.valueProperty()); - settings.debugMode().bind(debugModeCheckbox.selectedProperty()); - } - - @Override - public Parent getRoot() { - return root; - } - - @FXML - private void changePort() { - assert isPortValid() : "Button must be disabled, if port is invalid."; - try { - int port = Integer.parseInt(portField.getText()); - settings.port().set(port); - } catch (NumberFormatException e) { - throw new IllegalStateException("Button must be disabled, if port is invalid.", e); - } - } - - private boolean isPortValid() { - try { - int port = Integer.parseInt(portField.getText()); - return port == 0 // choose port automatically - || port >= Settings.MIN_PORT && port <= Settings.MAX_PORT; // port within range - } catch (NumberFormatException e) { - return false; - } - } - - private void filterNumericKeyEvents(KeyEvent t) { - if (!Strings.isNullOrEmpty(t.getCharacter()) && !DIGITS_MATCHER.matchesAllOf(t.getCharacter())) { - t.consume(); - } - } - - private void setVisibilityGvfsElements(@SuppressWarnings("unused") Observable obs, @SuppressWarnings("unused")Object oldValue, Object newValue) { - prefGvfsSchemeLabel.setVisible(SystemUtils.IS_OS_LINUX && ((VolumeImpl) newValue).getDisplayName().equals("WebDAV")); - prefGvfsScheme.setVisible(SystemUtils.IS_OS_LINUX && ((VolumeImpl) newValue).getDisplayName().equals("WebDAV")); - } - - private boolean areUpdatesManagedExternally() { - return Boolean.parseBoolean(System.getProperty("cryptomator.updatesManagedExternally", "false")); - } - - private static class NioAdapterImplStringConverter extends StringConverter { - - @Override - public String toString(VolumeImpl object) { - return object.getDisplayName(); - } - - @Override - public VolumeImpl fromString(String string) { - return VolumeImpl.forDisplayName(string); - } - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java deleted file mode 100644 index 0af76805e..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockController.java +++ /dev/null @@ -1,581 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014, 2017 Sebastian Stenzel - * All rights reserved. - * This program and the accompanying materials are made available under the terms of the accompanying LICENSE file. - * - * Contributors: - * Sebastian Stenzel - initial API and implementation - ******************************************************************************/ -package org.cryptomator.ui.controllers; - -import com.google.common.base.CharMatcher; -import com.google.common.base.Strings; -import javafx.application.Application; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.fxml.FXML; -import javafx.scene.Parent; -import javafx.scene.control.Alert; -import javafx.scene.control.Button; -import javafx.scene.control.ButtonType; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ChoiceBox; -import javafx.scene.control.ContentDisplay; -import javafx.scene.control.Hyperlink; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.input.KeyEvent; -import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; -import javafx.scene.text.Text; -import javafx.stage.DirectoryChooser; -import javafx.stage.Stage; -import javafx.util.StringConverter; -import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.common.settings.Settings; -import org.cryptomator.common.settings.VaultSettings; -import org.cryptomator.common.settings.VolumeImpl; -import org.cryptomator.cryptolib.api.InvalidPassphraseException; -import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException; -import org.cryptomator.keychain.KeychainAccess; -import org.cryptomator.keychain.KeychainAccessException; -import org.cryptomator.ui.controls.SecurePasswordField; -import org.cryptomator.ui.l10n.Localization; -import org.cryptomator.common.vaults.Vault; -import org.cryptomator.common.vaults.WindowsDriveLetters; -import org.cryptomator.ui.util.DialogBuilderUtil; -import org.cryptomator.ui.common.Tasks; -import org.fxmisc.easybind.EasyBind; -import org.fxmisc.easybind.Subscription; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import javax.inject.Named; -import java.io.File; -import java.nio.file.DirectoryNotEmptyException; -import java.nio.file.NotDirectoryException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.ExecutorService; - -public class UnlockController implements ViewController { - - private static final Logger LOG = LoggerFactory.getLogger(UnlockController.class); - private static final CharMatcher ALPHA_NUMERIC_MATCHER = CharMatcher.inRange('a', 'z') // - .or(CharMatcher.inRange('A', 'Z')) // - .or(CharMatcher.inRange('0', '9')) // - .or(CharMatcher.is('_')) // - .precomputed(); - - private final Application app; - private final Stage mainWindow; - private final Localization localization; - private final WindowsDriveLetters driveLetters; - private final ChangeListener driveLetterChangeListener = this::winDriveLetterDidChange; - private final Optional keychainAccess; - private final Settings settings; - private final ExecutorService executor; - private Vault vault; - private Optional listener = Optional.empty(); - private Subscription vaultSubs = Subscription.EMPTY; - private BooleanProperty unlocking = new SimpleBooleanProperty(); - - @Inject - public UnlockController(Application app, @Named("mainWindow") Stage mainWindow, Localization localization, WindowsDriveLetters driveLetters, Optional keychainAccess, Settings settings, ExecutorService executor) { - this.app = app; - this.mainWindow = mainWindow; - this.localization = localization; - this.driveLetters = driveLetters; - this.keychainAccess = keychainAccess; - this.settings = settings; - this.executor = executor; - } - - @FXML - private SecurePasswordField passwordField; - - @FXML - private Button advancedOptionsButton; - - @FXML - private Button unlockButton; - - @FXML - private Text messageText; - - @FXML - private CheckBox savePassword; - - @FXML - private TextField mountName; - - @FXML - private CheckBox useCustomMountFlags; - - @FXML - private TextField mountFlags; - - @FXML - private CheckBox revealAfterMount; - - @FXML - private CheckBox useCustomWinDriveLetter; - - @FXML - private ChoiceBox winDriveLetter; - - @FXML - private CheckBox useCustomMountPoint; - - @FXML - private HBox customMountPoint; - - @FXML - private Label customMountPointLabel; - - @FXML - private Hyperlink downloadsPageLink; - - @FXML - private VBox advancedOptions; - - @FXML - private VBox root; - - @FXML - private CheckBox unlockAfterStartup; - - @FXML - private CheckBox useReadOnlyMode; - - @Override - public void initialize() { - advancedOptions.managedProperty().bind(advancedOptions.visibleProperty()); - advancedOptions.disableProperty().bind(unlocking); - unlockButton.disableProperty().bind(unlocking.or(passwordField.textProperty().isEmpty())); - mountName.addEventFilter(KeyEvent.KEY_TYPED, this::filterAlphanumericKeyEvents); - mountName.textProperty().addListener(this::mountNameDidChange); - useReadOnlyMode.selectedProperty().addListener(this::useReadOnlyDidChange); - useCustomMountFlags.selectedProperty().addListener(this::useCustomMountFlagsDidChange); - mountFlags.disableProperty().bind(useCustomMountFlags.selectedProperty().not()); - mountFlags.textProperty().addListener(this::mountFlagsDidChange); - savePassword.setDisable(!keychainAccess.isPresent()); - unlockAfterStartup.disableProperty().bind(savePassword.disabledProperty().or(savePassword.selectedProperty().not())); - downloadsPageLink.visibleProperty().bind(downloadsPageLink.managedProperty()); - - customMountPoint.visibleProperty().bind(useCustomMountPoint.selectedProperty()); - customMountPoint.managedProperty().bind(useCustomMountPoint.selectedProperty()); - winDriveLetter.setConverter(new WinDriveLetterLabelConverter()); - winDriveLetter.disableProperty().bind(useCustomWinDriveLetter.selectedProperty().not()); - - if (!SystemUtils.IS_OS_WINDOWS) { - useCustomWinDriveLetter.setVisible(false); - useCustomWinDriveLetter.setManaged(false); - winDriveLetter.setVisible(false); - winDriveLetter.setManaged(false); - } - } - - @Override - public Parent getRoot() { - return root; - } - - @Override - public void focus() { - passwordField.requestFocus(); - } - - void setVault(Vault vault, State state) { - vaultSubs.unsubscribe(); - vaultSubs = Subscription.EMPTY; - - // trigger "default" change to refresh key bindings: - unlockButton.setDefaultButton(false); - unlockButton.setDefaultButton(true); - if (Objects.equals(this.vault, Objects.requireNonNull(vault))) { - return; - } - assert vault != null; - this.vault = vault; - advancedOptions.setVisible(false); - advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.show")); - unlockButton.setContentDisplay(ContentDisplay.TEXT_ONLY); - state.successMessage().map(localization::getString).ifPresent(messageText::setText); - downloadsPageLink.setManaged(false); - mountName.setText(vault.getMountName()); - useCustomMountFlags.setSelected(vault.isHavingCustomMountFlags()); - mountFlags.setText(vault.getEffectiveMountFlags()); - savePassword.setSelected(false); - // auto-fill pw from keychain: - if (keychainAccess.isPresent()) { - try { - char[] storedPw = keychainAccess.get().loadPassphrase(vault.getId()); - if (storedPw != null) { - savePassword.setSelected(true); - passwordField.setPassword(storedPw); - passwordField.selectRange(storedPw.length, storedPw.length); - Arrays.fill(storedPw, ' '); - } - } catch (KeychainAccessException e) { - LOG.error("Failed to load stored password from system keychain.", e); - } - } - VaultSettings vaultSettings = vault.getVaultSettings(); - unlockAfterStartup.setSelected(savePassword.isSelected() && vaultSettings.unlockAfterStartup().get()); - revealAfterMount.setSelected(vaultSettings.revealAfterMount().get()); - useReadOnlyMode.setSelected(vaultSettings.usesReadOnlyMode().get()); - - // WEBDAV-dependent controls: - if (VolumeImpl.WEBDAV.equals(settings.preferredVolumeImpl().get())) { - useCustomMountPoint.setVisible(false); - useCustomMountPoint.setManaged(false); - useCustomMountFlags.setVisible(false); - useCustomMountFlags.setManaged(false); - mountFlags.setVisible(false); - mountFlags.setManaged(false); - } else { - useCustomMountPoint.setVisible(true); - useCustomMountPoint.setSelected(vaultSettings.usesIndividualMountPath().get()); - if (Strings.isNullOrEmpty(vaultSettings.individualMountPath().get())) { - customMountPointLabel.setText(localization.getString("unlock.label.chooseMountPath")); - } else { - customMountPointLabel.setText(displayablePath(vaultSettings.individualMountPath().getValueSafe())); - } - } - - - // OS-dependent controls: - if (SystemUtils.IS_OS_WINDOWS) { - winDriveLetter.valueProperty().removeListener(driveLetterChangeListener); - winDriveLetter.getItems().clear(); - winDriveLetter.getItems().add(null); - winDriveLetter.getItems().addAll(driveLetters.getAvailableDriveLetters()); - winDriveLetter.getItems().sort(new WinDriveLetterComparator()); - winDriveLetter.valueProperty().addListener(driveLetterChangeListener); - chooseSelectedDriveLetter(); - - winDriveLetter.visibleProperty().bind(useCustomMountPoint.selectedProperty().not()); - winDriveLetter.managedProperty().bind(useCustomMountPoint.selectedProperty().not()); - useCustomWinDriveLetter.setSelected(!vaultSettings.usesIndividualMountPath().get()); - useCustomWinDriveLetter.visibleProperty().bind(useCustomMountPoint.selectedProperty().not()); - useCustomWinDriveLetter.managedProperty().bind(useCustomMountPoint.selectedProperty().not()); - } - - vaultSubs = vaultSubs.and(EasyBind.subscribe(unlockAfterStartup.selectedProperty(), vaultSettings.unlockAfterStartup()::set)); - vaultSubs = vaultSubs.and(EasyBind.subscribe(revealAfterMount.selectedProperty(), vaultSettings.revealAfterMount()::set)); - vaultSubs = vaultSubs.and(EasyBind.subscribe(useCustomMountPoint.selectedProperty(), vaultSettings.usesIndividualMountPath()::set)); - vaultSubs = vaultSubs.and(EasyBind.subscribe(useReadOnlyMode.selectedProperty(), vaultSettings.usesReadOnlyMode()::set)); - } - - private String displayablePath(String path) { - Path homeDir = Paths.get(SystemUtils.USER_HOME); - Path p = Paths.get(path); - if (p.startsWith(homeDir)) { - Path relativePath = homeDir.relativize(p); - String homePrefix = SystemUtils.IS_OS_WINDOWS ? "~\\" : "~/"; - return homePrefix + relativePath.toString(); - } else { - return p.toString(); - } - } - - // **************************************** - // Downloads link - // **************************************** - - @FXML - public void didClickDownloadsLink() { - app.getHostServices().showDocument("https://cryptomator.org/downloads/#allVersions"); - } - - // **************************************** - // Advanced options button - // **************************************** - - @FXML - private void didClickAdvancedOptionsButton() { - messageText.setText(null); - downloadsPageLink.setManaged(false); - advancedOptions.setVisible(!advancedOptions.isVisible()); - if (advancedOptions.isVisible()) { - advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.hide")); - } else { - advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.show")); - } - } - - private void filterAlphanumericKeyEvents(KeyEvent t) { - if (!Strings.isNullOrEmpty(t.getCharacter()) && !ALPHA_NUMERIC_MATCHER.matchesAllOf(t.getCharacter())) { - t.consume(); - } - } - - private void mountNameDidChange(@SuppressWarnings("unused") ObservableValue property, @SuppressWarnings("unused") String oldValue, String newValue) { - // newValue is guaranteed to be a-z0-9_, see #filterAlphanumericKeyEvents - if (newValue.isEmpty()) { - mountName.setText(vault.getMountName()); - } else { - vault.setMountName(newValue); - } - if (!useCustomMountFlags.isSelected()) { - mountFlags.setText(vault.getEffectiveMountFlags()); // update default flags - } - } - - private void useReadOnlyDidChange(@SuppressWarnings("unused") ObservableValue property, @SuppressWarnings("unused") Boolean oldValue, Boolean newValue) { - vault.getVaultSettings().usesReadOnlyMode().setValue(newValue); - if (!useCustomMountFlags.isSelected()) { - mountFlags.setText(vault.getEffectiveMountFlags()); // update default flags - } - } - - - private void useCustomMountFlagsDidChange(@SuppressWarnings("unused") ObservableValue property, @SuppressWarnings("unused") Boolean oldValue, Boolean newValue) { - if (!newValue) { - vault.setCustomMountFlags(VaultSettings.DEFAULT_MOUNT_FLAGS); - mountFlags.setText(vault.getEffectiveMountFlags()); - } - } - - private void mountFlagsDidChange(@SuppressWarnings("unused") ObservableValue property, @SuppressWarnings("unused") String oldValue, String newValue) { - if (useCustomMountFlags.isSelected()) { - vault.setCustomMountFlags(newValue); - } - } - - @FXML - public void didClickChooseCustomMountPoint() { - DirectoryChooser dirChooser = new DirectoryChooser(); - File file = dirChooser.showDialog(mainWindow); - if (file != null) { - customMountPointLabel.setText(displayablePath(file.toString())); - vault.setCustomMountPath(file.toString()); - } - } - - @FXML - public void didClickCustomWinDriveLetterCheckbox() { - if (!useCustomWinDriveLetter.isSelected()) { - winDriveLetter.setValue(null); - } - } - - @FXML - public void didClickCustomMountPointCheckbox() { - useCustomWinDriveLetter.setSelected(vault.getWinDriveLetter() != null); - } - - /** - * Converts 'C' to "C:" to translate between model and GUI. - */ - private class WinDriveLetterLabelConverter extends StringConverter { - - @Override - public String toString(Path root) { - if (root == null) { - return localization.getString("unlock.choicebox.winDriveLetter.auto"); - } else if (root.endsWith("occupied")) { - return root.getRoot().toString().substring(0, 1) + " (" + localization.getString("unlock.choicebox.winDriveLetter.occupied") + ")"; - } else { - return root.toString().substring(0, 1); - } - } - - @Override - public Path fromString(String string) { - if (localization.getString("unlock.choicebox.winDriveLetter.auto").equals(string)) { - return null; - } else { - return Path.of(string); - } - } - - } - - /** - * Natural sorting of ASCII letters, but null always on first, as this is "auto-assign". - */ - private static class WinDriveLetterComparator implements Comparator { - - @Override - public int compare(Path c1, Path c2) { - if (c1 == null) { - return -1; - } else if (c2 == null) { - return 1; - } else { - return c1.compareTo(c2); - } - } - } - - private void winDriveLetterDidChange(@SuppressWarnings("unused") ObservableValue property, @SuppressWarnings("unused") Path oldValue, Path newValue) { - vault.setWinDriveLetter(newValue); - } - - private void chooseSelectedDriveLetter() { - assert SystemUtils.IS_OS_WINDOWS; - // if the vault prefers a drive letter, that is currently occupied, this is our last chance to reset this: - if (vault.getWinDriveLetter() != null) { - final Path pickedRoot = Path.of(vault.getWinDriveLetter() + ":\\"); - if (driveLetters.getOccupiedDriveLetters().contains(pickedRoot)) { - Path alteredPath = pickedRoot.resolve("occupied"); - this.winDriveLetter.getItems().add(alteredPath); - this.winDriveLetter.getSelectionModel().select(alteredPath); - } else { - this.winDriveLetter.getSelectionModel().select(pickedRoot); - } - } else { - // first option is known to be 'auto-assign' due to #WinDriveLetterComparator. - this.winDriveLetter.getSelectionModel().selectFirst(); - } - - } - - // **************************************** - // Save password checkbox - // **************************************** - - @FXML - private void didClickSavePasswordCheckbox() { - if (!savePassword.isSelected() && hasStoredPassword()) { - Alert confirmDialog = DialogBuilderUtil.buildConfirmationDialog( // - localization.getString("unlock.savePassword.delete.confirmation.title"), // - localization.getString("unlock.savePassword.delete.confirmation.header"), // - localization.getString("unlock.savePassword.delete.confirmation.content"), // - SystemUtils.IS_OS_MAC_OSX ? ButtonType.CANCEL : ButtonType.OK); - - Optional choice = confirmDialog.showAndWait(); - if (ButtonType.OK.equals(choice.get())) { - try { - keychainAccess.get().deletePassphrase(vault.getId()); - } catch (KeychainAccessException e) { - LOG.error("Failed to remove entry from system keychain.", e); - } - } else if (ButtonType.CANCEL.equals(choice.get())) { - savePassword.setSelected(true); - } - } - } - - private boolean hasStoredPassword() { - try { - char[] storedPw = keychainAccess.get().loadPassphrase(vault.getId()); - boolean hasPw = (storedPw != null); - if (storedPw != null) { - Arrays.fill(storedPw, ' '); - } - return hasPw; - } catch (KeychainAccessException e) { - return false; - } - } - - // **************************************** - // Unlock button - // **************************************** - - @FXML - private void didClickUnlockButton() { - unlocking.set(true); - advancedOptionsButton.setText(localization.getString("unlock.button.advancedOptions.show")); - unlockButton.setContentDisplay(ContentDisplay.LEFT); - - CharSequence password = passwordField.getCharacters(); - Tasks.create(() -> { - vault.unlock(password); - if (keychainAccess.isPresent() && savePassword.isSelected()) { - keychainAccess.get().storePassphrase(vault.getId(), password); - } - }).onSuccess(() -> { - messageText.setText(null); - downloadsPageLink.setManaged(false); - listener.ifPresent(lstnr -> lstnr.didUnlock(vault)); - passwordField.swipe(); - }).onError(InvalidPassphraseException.class, e -> { - messageText.setText(localization.getString("unlock.errorMessage.wrongPassword")); - downloadsPageLink.setManaged(false); - passwordField.selectAll(); - passwordField.requestFocus(); - }).onError(UnsupportedVaultFormatException.class, e -> { - if (e.isVaultOlderThanSoftware()) { - // whitespace after localized text used as separator between text and hyperlink - messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.vaultOlderThanSoftware") + " "); - downloadsPageLink.setManaged(true); - } else if (e.isSoftwareOlderThanVault()) { - messageText.setText(localization.getString("unlock.errorMessage.unsupportedVersion.softwareOlderThanVault") + " "); - downloadsPageLink.setManaged(true); - } else if (e.getDetectedVersion() == Integer.MAX_VALUE) { - messageText.setText(localization.getString("unlock.errorMessage.unauthenticVersionMac")); - } - }).onError(NotDirectoryException.class, e -> { - LOG.error("Unlock failed. Mount point not a directory: {}", e.getMessage()); - advancedOptions.setVisible(true); - messageText.setText(null); - downloadsPageLink.setManaged(false); - showUnlockFailedErrorDialog("unlock.failedDialog.content.mountPathNonExisting"); - }).onError(DirectoryNotEmptyException.class, e -> { - LOG.error("Unlock failed. Mount point not empty: {}", e.getMessage()); - advancedOptions.setVisible(true); - messageText.setText(null); - downloadsPageLink.setManaged(false); - showUnlockFailedErrorDialog("unlock.failedDialog.content.mountPathNotEmpty"); - }).onError(Exception.class, e -> { // including RuntimeExceptions - LOG.error("Unlock failed for technical reasons.", e); - messageText.setText(localization.getString("unlock.errorMessage.unlockFailed")); - downloadsPageLink.setManaged(false); - }).andFinally(() -> { - unlocking.set(false); - unlockButton.setContentDisplay(ContentDisplay.TEXT_ONLY); - }).runOnce(executor); - } - - private void showUnlockFailedErrorDialog(String localizableContentKey) { - String title = localization.getString("unlock.failedDialog.title"); - String header = localization.getString("unlock.failedDialog.header"); - String content = localization.getString(localizableContentKey); - Alert alert = DialogBuilderUtil.buildErrorDialog(title, header, content, ButtonType.OK); - alert.show(); - } - - /* callback */ - - public void setListener(UnlockListener listener) { - this.listener = Optional.ofNullable(listener); - } - - @FunctionalInterface - interface UnlockListener { - - void didUnlock(Vault vault); - } - - /* state */ - - public enum State { - UNLOCKING(null), // - INITIALIZED("unlock.successLabel.vaultCreated"), // - PASSWORD_CHANGED("unlock.successLabel.passwordChanged"), // - UPGRADED("unlock.successLabel.upgraded"); - - private Optional successMessage; - - State(String successMessage) { - this.successMessage = Optional.ofNullable(successMessage); - } - - public Optional successMessage() { - return successMessage; - } - - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java deleted file mode 100644 index 27361f093..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UnlockedController.java +++ /dev/null @@ -1,275 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014, 2017 Sebastian Stenzel - * All rights reserved. - * This program and the accompanying materials are made available under the terms of the accompanying LICENSE file. - * - * Contributors: - * Sebastian Stenzel - initial API and implementation - ******************************************************************************/ -package org.cryptomator.ui.controllers; - -import javafx.animation.Animation; -import javafx.animation.KeyFrame; -import javafx.animation.Timeline; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.event.ActionEvent; -import javafx.event.EventHandler; -import javafx.fxml.FXML; -import javafx.geometry.Side; -import javafx.scene.Parent; -import javafx.scene.chart.LineChart; -import javafx.scene.chart.NumberAxis; -import javafx.scene.chart.XYChart.Data; -import javafx.scene.chart.XYChart.Series; -import javafx.scene.control.Alert; -import javafx.scene.control.ButtonType; -import javafx.scene.control.ContextMenu; -import javafx.scene.control.Label; -import javafx.scene.control.ToggleButton; -import javafx.scene.layout.VBox; -import javafx.stage.PopupWindow.AnchorLocation; -import javafx.util.Duration; -import org.cryptomator.ui.l10n.Localization; -import org.cryptomator.common.vaults.Vault; -import org.cryptomator.ui.util.DialogBuilderUtil; -import org.cryptomator.ui.common.Tasks; -import org.fxmisc.easybind.EasyBind; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.util.Optional; -import java.util.concurrent.ExecutorService; - -import static java.lang.String.format; - -public class UnlockedController implements ViewController { - - private static final Logger LOG = LoggerFactory.getLogger(UnlockedController.class); - - private static final int IO_SAMPLING_STEPS = 100; - private static final double IO_SAMPLING_INTERVAL = 0.5; - - private final Localization localization; - private final ExecutorService executor; - private final ObjectProperty vault = new SimpleObjectProperty<>(); - private Optional listener = Optional.empty(); - private Timeline ioAnimation; - - @FXML - private Label messageLabel; - - @FXML - private LineChart ioGraph; - - @FXML - private NumberAxis xAxis; - - @FXML - private ToggleButton moreOptionsButton; - - @FXML - private ContextMenu moreOptionsMenu; - - @FXML - private VBox root; - - @Inject - public UnlockedController(Localization localization, ExecutorService executor) { - this.localization = localization; - this.executor = executor; - } - - @Override - public void initialize() { - EasyBind.subscribe(vault, this::vaultChanged); - EasyBind.subscribe(moreOptionsMenu.showingProperty(), moreOptionsButton::setSelected); - } - - @Override - public Parent getRoot() { - return root; - } - - private void vaultChanged(Vault newVault) { - if (newVault == null) { - return; - } - - // (re)start throughput statistics: - stopIoSampling(); - startIoSampling(); - } - - @FXML - private void didClickLockVault() { - regularLockVault(this::lockVaultSucceeded); - } - - private void lockVaultSucceeded() { - listener.ifPresent(listener -> listener.didLock(this)); - } - - private void regularLockVault(Runnable onSuccess) { - Tasks.create(() -> { - vault.get().lock(false); - }).onSuccess(() -> { - LOG.trace("Regular unmount succeeded."); - onSuccess.run(); - }).onError(Exception.class, e -> { - onRegularUnmountVaultFailed(e, onSuccess); - }).runOnce(executor); - } - - private void onRegularUnmountVaultFailed(Exception e, Runnable onSuccess) { - if (vault.get().supportsForcedUnmount()) { - LOG.trace("Regular unmount failed.", e); - Alert confirmDialog = DialogBuilderUtil.buildYesNoDialog( // - format(localization.getString("unlocked.lock.force.confirmation.title"), vault.get().name().getValue()), // - localization.getString("unlocked.lock.force.confirmation.header"), // - localization.getString("unlocked.lock.force.confirmation.content"), // - ButtonType.NO); - - Optional choice = confirmDialog.showAndWait(); - if (ButtonType.YES.equals(choice.get())) { - forcedLockVault(onSuccess); - } else { - LOG.trace("Unmount cancelled.", e); - } - } else { - LOG.error("Regular unmount failed.", e); - messageLabel.setText(localization.getString("unlocked.label.unmountFailed")); - } - } - - private void forcedLockVault(Runnable onSuccess) { - Tasks.create(() -> { - vault.get().lock(true); - }).onSuccess(() -> { - LOG.trace("Forced unmount succeeded."); - onSuccess.run(); - }).onError(Exception.class, e -> { - LOG.error("Forced unmount failed.", e); - messageLabel.setText(localization.getString("unlocked.label.unmountFailed")); - }).runOnce(executor); - } - - @FXML - private void didClickMoreOptions() { - if (moreOptionsMenu.isShowing()) { - moreOptionsMenu.hide(); - } else { - moreOptionsMenu.setAnchorLocation(AnchorLocation.CONTENT_TOP_RIGHT); - moreOptionsMenu.show(moreOptionsButton, Side.BOTTOM, moreOptionsButton.getWidth(), 0.0); - } - } - - @FXML - private void didClickRevealVault() { - revealVault(vault.get()); - } - - void revealVault(Vault vault) { - Tasks.create(() -> { - vault.reveal(); - }).onSuccess(() -> { - LOG.trace("Reveal succeeded."); - messageLabel.setText(null); - }).onError(Exception.class, e -> { - LOG.error("Reveal failed.", e); - messageLabel.setText(localization.getString("unlocked.label.revealFailed")); - }).runOnce(executor); - } - - // **************************************** - // IO Graph - // **************************************** - - private void startIoSampling() { - final Series decryptedBytes = new Series<>(); - decryptedBytes.setName(localization.getString("unlocked.label.statsDecrypted")); - final Series encryptedBytes = new Series<>(); - encryptedBytes.setName(localization.getString("unlocked.label.statsEncrypted")); - - ioGraph.getData().add(decryptedBytes); - ioGraph.getData().add(encryptedBytes); - - ioAnimation = new Timeline(); - ioAnimation.getKeyFrames().add(new KeyFrame(Duration.seconds(IO_SAMPLING_INTERVAL), new IoSamplingAnimationHandler(decryptedBytes, encryptedBytes))); - ioAnimation.setCycleCount(Animation.INDEFINITE); - ioAnimation.play(); - } - - private void stopIoSampling() { - if (ioAnimation != null) { - ioGraph.getData().clear(); - ioAnimation.stop(); - } - } - - private class IoSamplingAnimationHandler implements EventHandler { - - private static final double BYTES_TO_MEGABYTES_FACTOR = 1.0 / IO_SAMPLING_INTERVAL / 1024.0 / 1024.0; - private final Series decryptedBytes; - private final Series encryptedBytes; - - public IoSamplingAnimationHandler(Series decryptedBytes, Series encryptedBytes) { - this.decryptedBytes = decryptedBytes; - this.encryptedBytes = encryptedBytes; - - // initialize data once and change value of datapoints later: - for (int i = 0; i < IO_SAMPLING_STEPS; i++) { - decryptedBytes.getData().add(new Data<>(i, 0)); - encryptedBytes.getData().add(new Data<>(i, 0)); - } - - xAxis.setLowerBound(0); - xAxis.setUpperBound(IO_SAMPLING_STEPS); - } - - @Override - public void handle(ActionEvent event) { - // move all values one step: - for (int i = 0; i < IO_SAMPLING_STEPS - 1; i++) { - int j = i + 1; - Number tmp = decryptedBytes.getData().get(j).getYValue(); - decryptedBytes.getData().get(i).setYValue(tmp); - - tmp = encryptedBytes.getData().get(j).getYValue(); - encryptedBytes.getData().get(i).setYValue(tmp); - } - - // add latest value: - final long decBytes = vault.get().pollBytesRead(); - final double decMb = decBytes * BYTES_TO_MEGABYTES_FACTOR; - final long encBytes = vault.get().pollBytesWritten(); - final double encMb = encBytes * BYTES_TO_MEGABYTES_FACTOR; - decryptedBytes.getData().get(IO_SAMPLING_STEPS - 1).setYValue(decMb); - encryptedBytes.getData().get(IO_SAMPLING_STEPS - 1).setYValue(encMb); - } - } - - /* Getter/Setter */ - - public Vault getVault() { - return this.vault.get(); - } - - public void setVault(Vault vault) { - this.vault.set(vault); - } - - /* callback */ - - public void setListener(LockListener listener) { - this.listener = Optional.ofNullable(listener); - } - - @FunctionalInterface - interface LockListener { - - void didLock(UnlockedController ctrl); - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/UpgradeController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/UpgradeController.java deleted file mode 100644 index ca35ec57b..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/UpgradeController.java +++ /dev/null @@ -1,162 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.ui.controllers; - -import javax.inject.Inject; -import java.util.Objects; -import java.util.Optional; -import java.util.concurrent.ExecutorService; - -import javafx.beans.binding.BooleanExpression; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.Parent; -import javafx.scene.control.Button; -import javafx.scene.control.CheckBox; -import javafx.scene.control.Label; -import javafx.scene.control.ProgressIndicator; -import javafx.scene.layout.GridPane; -import org.cryptomator.ui.controls.SecurePasswordField; -import org.cryptomator.ui.model.upgrade.UpgradeStrategies; -import org.cryptomator.ui.model.upgrade.UpgradeStrategy; -import org.cryptomator.ui.model.upgrade.UpgradeStrategy.UpgradeFailedException; -import org.cryptomator.common.vaults.Vault; -import org.cryptomator.ui.common.Tasks; -import org.fxmisc.easybind.EasyBind; - -public class UpgradeController implements ViewController { - - private final ObjectProperty strategy = new SimpleObjectProperty<>(); - private final UpgradeStrategies strategies; - private final ExecutorService executor; - private Optional listener = Optional.empty(); - private Vault vault; - - @Inject - public UpgradeController(UpgradeStrategies strategies, ExecutorService executor) { - this.strategies = strategies; - this.executor = executor; - } - - @FXML - private Label upgradeTitleLabel; - - @FXML - private Label upgradeMsgLabel; - - @FXML - private SecurePasswordField passwordField; - - @FXML - private CheckBox confirmationCheckbox; - - @FXML - private Button upgradeButton; - - @FXML - private ProgressIndicator progressIndicator; - - @FXML - private Label errorLabel; - - @FXML - private GridPane root; - - @Override - public void initialize() { - upgradeTitleLabel.textProperty().bind(EasyBind.monadic(strategy).map(this::upgradeTitle).orElse("")); - upgradeMsgLabel.textProperty().bind(EasyBind.monadic(strategy).map(this::upgradeMessage).orElse("")); - - BooleanExpression passwordProvided = passwordField.textProperty().isNotEmpty().and(passwordField.disabledProperty().not()); - BooleanExpression syncFinished = confirmationCheckbox.selectedProperty(); - upgradeButton.disableProperty().bind(passwordProvided.not().or(syncFinished.not())); - } - - @Override - public Parent getRoot() { - return root; - } - - @Override - public void focus() { - passwordField.requestFocus(); - } - - void setVault(Vault vault) { - this.vault = Objects.requireNonNull(vault); - errorLabel.setText(null); - strategy.set(strategies.getUpgradeStrategy(vault)); - // trigger "default" change to refresh key bindings: - upgradeButton.setDefaultButton(false); - upgradeButton.setDefaultButton(true); - } - - // **************************************** - // Upgrade label - // **************************************** - - private String upgradeTitle(UpgradeStrategy instruction) { - return instruction.getTitle(vault); - } - - private String upgradeMessage(UpgradeStrategy instruction) { - return instruction.getMessage(vault); - } - - // **************************************** - // Upgrade button - // **************************************** - - @FXML - private void didClickUpgradeButton(ActionEvent event) { - EasyBind.monadic(strategy).ifPresent(this::upgrade); - } - - private void upgrade(UpgradeStrategy instruction) { - passwordField.setDisable(true); - progressIndicator.setVisible(true); - Tasks // - .create(() -> { - if (!instruction.isApplicable(vault)) { - throw new IllegalStateException("No ugprade needed for " + vault.getPath()); - } - instruction.upgrade(vault, passwordField.getCharacters()); - }) // - .onSuccess(this::showNextUpgrade) // - .onError(UpgradeFailedException.class, e -> { - errorLabel.setText(e.getLocalizedMessage()); - }) // - .andFinally(() -> { - progressIndicator.setVisible(false); - passwordField.setDisable(false); - passwordField.swipe(); - }).runOnce(executor); - } - - private void showNextUpgrade() { - errorLabel.setText(null); - UpgradeStrategy nextStrategy = strategies.getUpgradeStrategy(vault); - if (nextStrategy != null) { - strategy.set(nextStrategy); - } else { - listener.ifPresent(UpgradeListener::didUpgrade); - } - } - - /* callback */ - - public void setListener(UpgradeListener listener) { - this.listener = Optional.ofNullable(listener); - } - - @FunctionalInterface - interface UpgradeListener { - void didUpgrade(); - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewController.java deleted file mode 100644 index 1f1ef817e..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewController.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.ui.controllers; - -import java.net.URL; -import java.util.ResourceBundle; - -import javafx.fxml.Initializable; -import javafx.scene.Parent; - -public interface ViewController extends Initializable { - - Parent getRoot(); - - @Override - default void initialize(URL location, ResourceBundle resources) { - initialize(); - } - - default void initialize() { - // no-op - } - - default void focus() { - // no-op - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerKey.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerKey.java deleted file mode 100644 index 72af006ea..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerKey.java +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.ui.controllers; - -import static java.lang.annotation.ElementType.METHOD; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import dagger.MapKey; - -@Documented -@Target(METHOD) -@Retention(RUNTIME) -@MapKey -public @interface ViewControllerKey { - Class value(); -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerLoader.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerLoader.java deleted file mode 100644 index c012696d1..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerLoader.java +++ /dev/null @@ -1,51 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.ui.controllers; - -import javafx.fxml.FXMLLoader; -import org.cryptomator.ui.fxapp.FxApplicationScoped; -import org.cryptomator.ui.l10n.Localization; - -import javax.inject.Inject; -import javax.inject.Provider; -import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; -import java.util.Map; - -@FxApplicationScoped -public class ViewControllerLoader { - - private final Map, Provider> controllerProviders; - private final Localization localization; - - @Inject - public ViewControllerLoader(Map, Provider> controllerProviders, Localization localization) { - this.controllerProviders = controllerProviders; - this.localization = localization; - } - - public T load(String resourceName) { - FXMLLoader loader = new FXMLLoader(); - loader.setControllerFactory(this::constructController); - loader.setResources(localization); - try (InputStream in = getClass().getResourceAsStream(resourceName)) { - loader.load(in); - } catch (IOException e) { - throw new UncheckedIOException("Error loading FXML: " + resourceName, e); - } - return loader.getController(); - } - - private ViewController constructController(Class clazz) { - Provider ctrlProvider = controllerProviders.get(clazz); - if (ctrlProvider == null) { - throw new IllegalStateException("No provider for type " + clazz.getName() + " registered."); - } - return ctrlProvider.get(); - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerModule.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerModule.java deleted file mode 100644 index f688f95cf..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/ViewControllerModule.java +++ /dev/null @@ -1,78 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.ui.controllers; - -import dagger.Module; -import dagger.Provides; -import dagger.multibindings.IntoMap; - -@Module -public class ViewControllerModule { - - @Provides - @IntoMap - @ViewControllerKey(ChangePasswordController.class) - ViewController provideChangePasswordController(ChangePasswordController controller) { - return controller; - } - - @Provides - @IntoMap - @ViewControllerKey(InitializeController.class) - ViewController provideInitializeController(InitializeController controller) { - return controller; - } - - @Provides - @IntoMap - @ViewControllerKey(MainController.class) - ViewController provideMainController(MainController controller) { - return controller; - } - - @Provides - @IntoMap - @ViewControllerKey(NotFoundController.class) - ViewController provideNotFoundController(NotFoundController controller) { - return controller; - } - - @Provides - @IntoMap - @ViewControllerKey(SettingsController.class) - ViewController provideSettingsController(SettingsController controller) { - return controller; - } - - @Provides - @IntoMap - @ViewControllerKey(UnlockController.class) - ViewController provideUnlockController(UnlockController controller) { - return controller; - } - - @Provides - @IntoMap - @ViewControllerKey(UnlockedController.class) - ViewController provideUnlockedController(UnlockedController controller) { - return controller; - } - - @Provides - @IntoMap - @ViewControllerKey(UpgradeController.class) - ViewController provideUpgradeController(UpgradeController controller) { - return controller; - } - - @Provides - @IntoMap - @ViewControllerKey(WelcomeController.class) - ViewController provideWelcomeController(WelcomeController controller) { - return controller; - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java b/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java deleted file mode 100644 index b93ebc553..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/controllers/WelcomeController.java +++ /dev/null @@ -1,195 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014, 2017 Sebastian Stenzel - * All rights reserved. - * This program and the accompanying materials are made available under the terms of the accompanying LICENSE file. - * - * Contributors: - * Sebastian Stenzel - initial API and implementation - ******************************************************************************/ -package org.cryptomator.ui.controllers; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; -import javafx.application.Application; -import javafx.application.Platform; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.Node; -import javafx.scene.Parent; -import javafx.scene.control.ButtonType; -import javafx.scene.control.Hyperlink; -import javafx.scene.control.Label; -import javafx.scene.control.ProgressIndicator; -import javafx.scene.layout.VBox; -import org.apache.commons.lang3.SystemUtils; -import org.cryptomator.ui.fxapp.FxApplicationScoped; -import org.cryptomator.common.settings.Settings; -import org.cryptomator.ui.l10n.Localization; -import org.cryptomator.ui.common.Tasks; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import javax.inject.Named; -import java.io.ByteArrayOutputStream; -import java.io.InputStream; -import java.net.HttpURLConnection; -import java.net.URI; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.Comparator; -import java.util.Map; -import java.util.Optional; -import java.util.ResourceBundle; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import static org.cryptomator.ui.util.DialogBuilderUtil.buildYesNoDialog; - -@FxApplicationScoped -public class WelcomeController implements ViewController { - - private static final Logger LOG = LoggerFactory.getLogger(WelcomeController.class); - - private final Application app; - private final Optional applicationVersion; - private final Localization localization; - private final Settings settings; - private final Comparator semVerComparator; - private final ScheduledExecutorService executor; - - @Inject - public WelcomeController(Application app, @Named("applicationVersion") Optional applicationVersion, Localization localization, Settings settings, @Named("SemVer") Comparator semVerComparator, - ScheduledExecutorService executor) { - this.app = app; - this.applicationVersion = applicationVersion; - this.localization = localization; - this.settings = settings; - this.semVerComparator = semVerComparator; - this.executor = executor; - } - - @FXML - private Node checkForUpdatesContainer; - - @FXML - private Label checkForUpdatesStatus; - - @FXML - private ProgressIndicator checkForUpdatesIndicator; - - @FXML - private Hyperlink updateLink; - - @FXML - private VBox root; - - @Override - public void initialize(URL location, ResourceBundle resources) { - if (areUpdatesManagedExternally()) { - checkForUpdatesContainer.setVisible(false); - } else if (!settings.askedForUpdateCheck().get()) { - this.askForUpdateCheck(); - } else if (settings.checkForUpdates().get()) { - this.checkForUpdates(); - } - } - - @Override - public Parent getRoot() { - return root; - } - - // **************************************** - // Check for updates - // **************************************** - - private boolean areUpdatesManagedExternally() { - return Boolean.parseBoolean(System.getProperty("cryptomator.updatesManagedExternally", "false")); - } - - private void askForUpdateCheck() { - Tasks.create(() -> {}).onSuccess(() -> { - Optional result = buildYesNoDialog( - localization.getString("welcome.askForUpdateCheck.dialog.title"), - localization.getString("welcome.askForUpdateCheck.dialog.header"), - localization.getString("welcome.askForUpdateCheck.dialog.content"), - ButtonType.YES).showAndWait(); - if (result.isPresent()) { - settings.askedForUpdateCheck().set(true); - settings.checkForUpdates().set(result.get().equals(ButtonType.YES)); - } - if (settings.checkForUpdates().get()) { - this.checkForUpdates(); - } - }).scheduleOnce(executor, 1, TimeUnit.SECONDS); - } - - private void checkForUpdates() { - checkForUpdatesStatus.setText(localization.getString("welcome.checkForUpdates.label.currentlyChecking")); - checkForUpdatesIndicator.setVisible(true); - Tasks.create(() -> { - String userAgent = String.format("Cryptomator VersionChecker/%s %s %s (%s)", applicationVersion.orElse("SNAPSHOT"), SystemUtils.OS_NAME, SystemUtils.OS_VERSION, SystemUtils.OS_ARCH); - URL url = URI.create("https://api.cryptomator.org/updates/latestVersion.json").toURL(); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.addRequestProperty("User-Agent", userAgent); - conn.connect(); - try { - if (conn.getResponseCode() != HttpURLConnection.HTTP_OK) { - return Optional.empty(); - } - try (InputStream in = conn.getInputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream()) { - in.transferTo(out); - return Optional.of(out.toByteArray()); - } - } finally { - conn.disconnect(); - } - }).onSuccess(response -> { - response.ifPresent(bytes -> { - Gson gson = new GsonBuilder().setLenient().create(); - String json = new String(bytes, StandardCharsets.UTF_8); - Map map = gson.fromJson(json, new TypeToken>() { - }.getType()); - if (map != null) { - this.compareVersions(map); - } - }); - }).onError(Exception.class, e -> { - LOG.warn("Error checking for updates", e); - }).andFinally(() -> { - checkForUpdatesStatus.setText(""); - checkForUpdatesIndicator.setVisible(false); - }).runOnce(executor); - } - - private void compareVersions(final Map latestVersions) { - assert Platform.isFxApplicationThread(); - final String latestVersion; - if (SystemUtils.IS_OS_MAC_OSX) { - latestVersion = latestVersions.get("mac"); - } else if (SystemUtils.IS_OS_WINDOWS) { - latestVersion = latestVersions.get("win"); - } else if (SystemUtils.IS_OS_LINUX) { - latestVersion = latestVersions.get("linux"); - } else { - // no version check possible on unsupported OS - return; - } - final String currentVersion = applicationVersion.orElse(null); - LOG.info("Current version: {}, lastest version: {}", currentVersion, latestVersion); - if (currentVersion != null && semVerComparator.compare(currentVersion, latestVersion) < 0) { - final String msg = String.format(localization.getString("welcome.newVersionMessage"), latestVersion, currentVersion); - this.updateLink.setText(msg); - this.updateLink.setVisible(true); - this.updateLink.setDisable(false); - } - } - - @FXML - public void didClickUpdateLink(ActionEvent event) { - app.getHostServices().showDocument("https://cryptomator.org/"); - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/controls/DirectoryListCell.java b/main/ui/src/main/java/org/cryptomator/ui/controls/DirectoryListCell.java deleted file mode 100644 index b2fe76d4b..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/controls/DirectoryListCell.java +++ /dev/null @@ -1,107 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016, 2017 Sebastian Stenzel and others. - * All rights reserved. - * This program and the accompanying materials are made available under the terms of the accompanying LICENSE file. - * - * Contributors: - * Sebastian Stenzel - initial API and implementation - *******************************************************************************/ -package org.cryptomator.ui.controls; - -import org.cryptomator.common.vaults.Vault; -import org.cryptomator.common.vaults.VaultState; -import org.fxmisc.easybind.EasyBind; - -import javafx.beans.binding.ObjectExpression; -import javafx.geometry.Pos; -import javafx.scene.control.ContentDisplay; -import javafx.scene.control.ContextMenu; -import javafx.scene.control.Label; -import javafx.scene.control.OverrunStyle; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; -import javafx.scene.paint.Paint; - -@Deprecated(forRemoval = true, since = "1.5.0") -public class DirectoryListCell extends DraggableListCell { - - private static final Color UNLOCKED_ICON_COLOR = new Color(0.901, 0.494, 0.133, 1.0); - private final Label statusText = new Label(); - private final Label nameText = new Label(); - private final Label pathText = new Label(); - private final VBox vbox = new VBox(4.0, nameText, pathText); - private final HBox hbox = new HBox(6.0, statusText, vbox); - - private ContextMenu vaultContextMenu; - - public DirectoryListCell() { - ObjectExpression vaultState = ObjectExpression.objectExpression(EasyBind.select(itemProperty()).selectObject(Vault::stateProperty)); - - hbox.setAlignment(Pos.CENTER_LEFT); - hbox.setPrefWidth(1); - vbox.setFillWidth(true); - - nameText.textProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::name)); - nameText.textFillProperty().bind(this.textFillProperty()); - nameText.fontProperty().bind(this.fontProperty()); - - pathText.textProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::displayablePath)); - pathText.setTextOverrun(OverrunStyle.ELLIPSIS); - pathText.getStyleClass().add("detail-label"); - - statusText.textProperty().bind(EasyBind.map(vaultState, this::getStatusIconText)); - statusText.textFillProperty().bind(EasyBind.combine(vaultState, textFillProperty(), this::getStatusIconColor)); - statusText.setMinSize(16.0, 16.0); - statusText.setAlignment(Pos.CENTER); - statusText.getStyleClass().add("fontawesome"); - - tooltipProperty().bind(EasyBind.monadic(itemProperty()).flatMap(Vault::displayablePath).map(p -> new Tooltip(p.toString()))); - contextMenuProperty().bind(EasyBind.map(vaultState, this::getContextMenu)); - - setGraphic(hbox); - setContentDisplay(ContentDisplay.GRAPHIC_ONLY); - } - - private String getStatusIconText(VaultState state) { - if (state == null) { - return ""; - } - switch (state) { - case UNLOCKED: - case PROCESSING: - return "\uf09c"; - case LOCKED: - default: - return "\uf023"; - } - } - - private Paint getStatusIconColor(VaultState state, Paint lockedValue) { - if (state == null) { - return lockedValue; - } - switch (state) { - case UNLOCKED: - case PROCESSING: - return UNLOCKED_ICON_COLOR; - case LOCKED: - default: - return lockedValue; - } - } - - private ContextMenu getContextMenu(VaultState state) { - if (state == VaultState.LOCKED) { - return vaultContextMenu; - } else { - return null; - } - } - - public void setVaultContextMenu(ContextMenu contextMenu) { - this.vaultContextMenu = contextMenu; - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java b/main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java deleted file mode 100644 index 14a604883..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/model/AutoUnlocker.java +++ /dev/null @@ -1,100 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.ui.model; - -import javafx.collections.ObservableList; -import org.cryptomator.common.vaults.Vault; -import org.cryptomator.common.vaults.Volume; -import org.cryptomator.cryptolib.api.CryptoException; -import org.cryptomator.keychain.KeychainAccess; -import org.cryptomator.keychain.KeychainAccessException; -import org.cryptomator.ui.fxapp.FxApplicationScoped; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.io.IOException; -import java.nio.CharBuffer; -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.Optional; -import java.util.concurrent.ExecutorService; -import java.util.stream.Collectors; - -@Deprecated(forRemoval = true, since = "1.5.0") -@FxApplicationScoped -public class AutoUnlocker { - - private static final Logger LOG = LoggerFactory.getLogger(AutoUnlocker.class); - private static final int NAP_TIME_MILLIS = 500; - - private final Optional keychainAccess; - private final ObservableList vaults; - private final ExecutorService executor; - - @Inject - public AutoUnlocker(Optional keychainAccess, ObservableList vaults, ExecutorService executor) { - this.keychainAccess = keychainAccess; - this.vaults = vaults; - this.executor = executor; - } - - public void unlockAllSilently() { - Collection vaultsToUnlock = vaults.stream().filter(this::shouldUnlockAfterStartup).collect(Collectors.toList()); - if (keychainAccess.isPresent() && !vaultsToUnlock.isEmpty()) { - executor.submit(() -> unlockAll(vaultsToUnlock)); - } - } - - private boolean shouldUnlockAfterStartup(Vault vault) { - return vault.getVaultSettings().unlockAfterStartup().get(); - } - - private void unlockAll(Collection vaults) { - try { - Iterator iterator = vaults.iterator(); - assert iterator.hasNext() : "vaults must not be empty"; - unlockSilently(iterator.next()); - while (iterator.hasNext()) { - Thread.sleep(NAP_TIME_MILLIS); - unlockSilently(iterator.next()); - } - } catch (InterruptedException e) { - LOG.warn("Auto unlock thread interrupted."); - Thread.currentThread().interrupt(); - } - } - - private void unlockSilently(Vault vault) { - char[] storedPw = new char[0]; - try { - storedPw = keychainAccess.get().loadPassphrase(vault.getId()); - if (storedPw == null) { - LOG.warn("No passphrase stored in keychain for vault registered for auto unlocking: {}", vault.getPath()); - return; - } - vault.unlock(CharBuffer.wrap(storedPw)); - revealSilently(vault); - } catch (IOException | CryptoException | Volume.VolumeException | KeychainAccessException e) { - LOG.error("Auto unlock failed.", e); - } finally { - Arrays.fill(storedPw, ' '); - } - } - - private void revealSilently(Vault mountedVault) { - if (!mountedVault.getVaultSettings().revealAfterMount().get()) { - return; - } - try { - mountedVault.reveal(); - } catch (Volume.VolumeException e) { - LOG.error("Auto unlock succeded, but revealing the drive failed.", e); - } - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeStrategies.java b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeStrategies.java deleted file mode 100644 index 5b03777f8..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeStrategies.java +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.ui.model.upgrade; - -import org.cryptomator.ui.fxapp.FxApplicationScoped; -import org.cryptomator.common.vaults.Vault; - -import javax.inject.Inject; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.Objects; - -@FxApplicationScoped -public class UpgradeStrategies { - - private final Collection strategies; - - @Inject - public UpgradeStrategies(UpgradeVersion3DropBundleExtension upgrader1, UpgradeVersion3to4 upgrader2, UpgradeVersion4to5 upgrader3, UpgradeVersion5toX upgrader4) { - strategies = Collections.unmodifiableList(Arrays.asList(upgrader1, upgrader2, upgrader3, upgrader4)); - } - - public UpgradeStrategy getUpgradeStrategy(Vault vault) { - Objects.requireNonNull(vault); - return strategies.stream().filter(strategy -> { - return strategy.isApplicable(vault); - }).findFirst().orElse(null); - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeStrategy.java b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeStrategy.java deleted file mode 100644 index 26e4d1b04..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeStrategy.java +++ /dev/null @@ -1,138 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.ui.model.upgrade; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardCopyOption; -import java.nio.file.StandardOpenOption; -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; - -import org.cryptomator.cryptolib.api.Cryptor; -import org.cryptomator.cryptolib.api.CryptorProvider; -import org.cryptomator.cryptolib.api.InvalidPassphraseException; -import org.cryptomator.cryptolib.api.KeyFile; -import org.cryptomator.cryptolib.api.UnsupportedVaultFormatException; -import org.cryptomator.ui.l10n.Localization; -import org.cryptomator.common.vaults.Vault; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public abstract class UpgradeStrategy { - - private static final Logger LOG = LoggerFactory.getLogger(UpgradeStrategy.class); - private static final String MASTERKEY_FILENAME = "masterkey.cryptomator"; - private static final String MASTERKEY_BACKUP_FILENAME = "masterkey.cryptomator.bkup"; - - protected final CryptorProvider cryptorProvider; - protected final Localization localization; - protected final int vaultVersionBeforeUpgrade; - protected final int vaultVersionAfterUpgrade; - - UpgradeStrategy(CryptorProvider cryptorProvider, Localization localization, int vaultVersionBeforeUpgrade, int vaultVersionAfterUpgrade) { - this.cryptorProvider = cryptorProvider; - this.localization = localization; - this.vaultVersionBeforeUpgrade = vaultVersionBeforeUpgrade; - this.vaultVersionAfterUpgrade = vaultVersionAfterUpgrade; - } - - static SecureRandom strongSecureRandom() { - try { - return SecureRandom.getInstanceStrong(); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException("A strong algorithm must exist in every Java platform.", e); - } - } - - /** - * @return Localized title string to display to the user when an upgrade is needed. - */ - public abstract String getTitle(Vault vault); - - /** - * @return Localized message string to display to the user when an upgrade is needed. - */ - public abstract String getMessage(Vault vault); - - /** - * Upgrades a vault. Might take a moment, should be run in a background thread. - */ - public void upgrade(Vault vault, CharSequence passphrase) throws UpgradeFailedException { - LOG.info("Upgrading {} from {} to {}.", vault.getPath(), vaultVersionBeforeUpgrade, vaultVersionAfterUpgrade); - final Path masterkeyFileBeforeUpgrade = vault.getPath().resolve(MASTERKEY_FILENAME); - try (Cryptor cryptor = readMasterkeyFile(masterkeyFileBeforeUpgrade, passphrase)) { - // create backup, as soon as we know the password was correct: - Path masterkeyBackupFile = vault.getPath().resolve(MASTERKEY_BACKUP_FILENAME); - Files.copy(masterkeyFileBeforeUpgrade, masterkeyBackupFile, StandardCopyOption.REPLACE_EXISTING); - LOG.info("Backuped masterkey."); - // do stuff: - upgrade(vault, cryptor); - // write updated masterkey file: - Path masterkeyFileAfterUpgrade = vault.getPath().resolve(MASTERKEY_FILENAME); // path may have changed - writeMasterkeyFile(masterkeyFileAfterUpgrade, cryptor, passphrase); - LOG.info("Updated masterkey."); - } catch (InvalidPassphraseException e) { - throw new UpgradeFailedException(localization.getString("unlock.errorMessage.wrongPassword")); - } catch (UnsupportedVaultFormatException e) { - if (e.getDetectedVersion() == Integer.MAX_VALUE) { - LOG.warn("Version MAC authentication error in vault {}", vault.getPath()); - throw new UpgradeFailedException(localization.getString("unlock.errorMessage.unauthenticVersionMac")); - } else { - LOG.warn("Upgrade failed.", e); - throw new UpgradeFailedException("Upgrade failed. Details in log message."); - } - } catch (IOException e) { - LOG.warn("Upgrade failed.", e); - throw new UpgradeFailedException("Upgrade failed. Details in log message."); - } - } - - protected Cryptor readMasterkeyFile(Path masterkeyFile, CharSequence passphrase) throws UnsupportedVaultFormatException, InvalidPassphraseException, IOException { - byte[] fileContents = Files.readAllBytes(masterkeyFile); - KeyFile keyFile = KeyFile.parse(fileContents); - return cryptorProvider.createFromKeyFile(keyFile, passphrase, vaultVersionBeforeUpgrade); - } - - protected void writeMasterkeyFile(Path masterkeyFile, Cryptor cryptor, CharSequence passphrase) throws IOException { - byte[] fileContents = cryptor.writeKeysToMasterkeyFile(passphrase, vaultVersionAfterUpgrade).serialize(); - Files.write(masterkeyFile, fileContents, StandardOpenOption.TRUNCATE_EXISTING); - } - - protected abstract void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException; - - /** - * Determines in O(1), if an upgrade can be applied to a vault. - * - * @return true if and only if the vault can be migrated to a newer version without the risk of data losses. - */ - public boolean isApplicable(Vault vault) { - final Path masterkeyFile = vault.getPath().resolve(MASTERKEY_FILENAME); - try { - byte[] masterkeyFileContents = Files.readAllBytes(masterkeyFile); - return KeyFile.parse(masterkeyFileContents).getVersion() == vaultVersionBeforeUpgrade; - } catch (IOException e) { - LOG.warn("Could not determine, whether upgrade is applicable.", e); - return false; - } - } - - /** - * Thrown when data migration failed. - */ - public static class UpgradeFailedException extends Exception { - - UpgradeFailedException() { - } - - UpgradeFailedException(String message) { - super(message); - } - - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion3DropBundleExtension.java b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion3DropBundleExtension.java deleted file mode 100644 index 5f60ffc6e..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion3DropBundleExtension.java +++ /dev/null @@ -1,81 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.ui.model.upgrade; - -import javafx.application.Platform; -import org.apache.commons.lang3.StringUtils; -import org.cryptomator.ui.fxapp.FxApplicationScoped; -import org.cryptomator.cryptolib.Cryptors; -import org.cryptomator.cryptolib.api.Cryptor; -import org.cryptomator.ui.l10n.Localization; -import org.cryptomator.common.vaults.Vault; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; - -@FxApplicationScoped -class UpgradeVersion3DropBundleExtension extends UpgradeStrategy { - - private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion3DropBundleExtension.class); - - @Inject - public UpgradeVersion3DropBundleExtension(Localization localization) { - super(Cryptors.version1(UpgradeStrategy.strongSecureRandom()), localization, 3, 3); - } - - @Override - public String getTitle(Vault vault) { - return localization.getString("upgrade.version3dropBundleExtension.title"); - } - - @Override - public String getMessage(Vault vault) { - String fmt = localization.getString("upgrade.version3dropBundleExtension.msg"); - Path path = vault.getPath(); - String oldVaultName = path.getFileName().toString(); - String newVaultName = StringUtils.removeEnd(oldVaultName, ".cryptomator"); - return String.format(fmt, oldVaultName, newVaultName); - } - - @Override - protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException { - Path path = vault.getPath(); - String oldVaultName = path.getFileName().toString(); - String newVaultName = StringUtils.removeEnd(oldVaultName, ".cryptomator"); - Path newPath = path.resolveSibling(newVaultName); - if (Files.exists(newPath)) { - String fmt = localization.getString("upgrade.version3dropBundleExtension.err.alreadyExists"); - String msg = String.format(fmt, newPath); - throw new UpgradeFailedException(msg); - } else { - try { - LOG.info("Renaming {} to {}", path, newPath.getFileName()); - Files.move(path, path.resolveSibling(newVaultName)); - Platform.runLater(() -> { - vault.getVaultSettings().path().set(newPath); - }); - } catch (IOException e) { - LOG.error("Vault migration failed", e); - throw new UpgradeFailedException(); - } - } - } - - @Override - public boolean isApplicable(Vault vault) { - Path vaultPath = vault.getPath(); - if (vaultPath.toString().endsWith(".cryptomator")) { - return super.isApplicable(vault); - } else { - return false; - } - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion3to4.java b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion3to4.java deleted file mode 100644 index e0f3b02e6..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion3to4.java +++ /dev/null @@ -1,171 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.ui.model.upgrade; - -import com.google.common.io.BaseEncoding; -import org.apache.commons.lang3.StringUtils; -import org.cryptomator.ui.fxapp.FxApplicationScoped; -import org.cryptomator.cryptolib.Cryptors; -import org.cryptomator.cryptolib.api.Cryptor; -import org.cryptomator.cryptolib.common.MessageDigestSupplier; -import org.cryptomator.ui.l10n.Localization; -import org.cryptomator.common.vaults.Vault; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.io.IOException; -import java.nio.file.FileVisitOption; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.security.MessageDigest; -import java.util.EnumSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static java.nio.charset.StandardCharsets.UTF_8; - -/** - * Contains the collective knowledge of all creatures who were alive during the development of vault format 3. - * This class uses no external classes from the crypto or shortening layer by purpose, so we don't need legacy code inside these. - */ -@FxApplicationScoped -class UpgradeVersion3to4 extends UpgradeStrategy { - - private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion3to4.class); - private static final Pattern LVL1_DIR_PATTERN = Pattern.compile("[A-Z2-7]{2}"); - private static final Pattern LVL2_DIR_PATTERN = Pattern.compile("[A-Z2-7]{30}"); - private static final Pattern BASE32_PATTERN = Pattern.compile("^(([A-Z2-7]{8})*[A-Z2-7=]{8})"); - private static final Pattern BASE32_FOLLOWED_BY_UNDERSCORE_PATTERN = Pattern.compile(BASE32_PATTERN.pattern() + "_"); - private static final int FILE_MIN_SIZE = 88; // vault version 3 files have a header of 88 bytes (assuming no chunks at all) - private static final String LONG_FILENAME_EXT = ".lng"; - private static final String OLD_FOLDER_SUFFIX = "_"; - private static final String NEW_FOLDER_PREFIX = "0"; - - private final MessageDigest sha1 = MessageDigestSupplier.SHA1.get(); - private final BaseEncoding base32 = BaseEncoding.base32(); - - @Inject - public UpgradeVersion3to4(Localization localization) { - super(Cryptors.version1(UpgradeStrategy.strongSecureRandom()), localization, 3, 4); - } - - @Override - public String getTitle(Vault vault) { - return localization.getString("upgrade.version3to4.title"); - } - - @Override - public String getMessage(Vault vault) { - return localization.getString("upgrade.version3to4.msg"); - } - - @Override - protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException { - Path dataDir = vault.getPath().resolve("d"); - Path metadataDir = vault.getPath().resolve("m"); - if (!Files.isDirectory(dataDir)) { - return; // empty vault. no migration needed. - } - try { - Files.walkFileTree(dataDir, EnumSet.noneOf(FileVisitOption.class), 3, new SimpleFileVisitor() { - - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - if (dir.equals(dataDir)) { - // path/to/vault/d - return FileVisitResult.CONTINUE; - } else if (dir.getParent().equals(dataDir) && LVL1_DIR_PATTERN.matcher(dir.getFileName().toString()).matches()) { - // path/to/vault/d/AB - return FileVisitResult.CONTINUE; - } else if (dir.getParent().getParent().equals(dataDir) && LVL2_DIR_PATTERN.matcher(dir.getFileName().toString()).matches()) { - // path/to/vault/d/AB/CDEFGHIJKLMNOPQRSTUVWXYZ234567 - return FileVisitResult.CONTINUE; - } else { - LOG.info("Skipping irrelevant directory {}", dir); - return FileVisitResult.SKIP_SUBTREE; - } - } - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - String name = file.getFileName().toString(); - if (attrs.size() > FILE_MIN_SIZE) { - LOG.trace("Skipping non-directory file {}.", file); - } else if (name.endsWith(LONG_FILENAME_EXT)) { - migrateLong(metadataDir, file); - } else { - migrate(file); - } - return FileVisitResult.CONTINUE; - } - - }); - } catch (IOException e) { - LOG.error("Migration failed.", e); - throw new UpgradeFailedException(localization.getString("upgrade.version3to4.err.io")); - } - LOG.info("Migration finished."); - } - - private void migrate(Path file) throws IOException { - String name = file.getFileName().toString(); - Matcher m = BASE32_FOLLOWED_BY_UNDERSCORE_PATTERN.matcher(name); - if (m.find(0)) { - String base32 = m.group(1); - String suffix = name.substring(m.end()); - String renamed = NEW_FOLDER_PREFIX + base32 + suffix; - renameWithoutOverwriting(file, renamed); - } - } - - private void renameWithoutOverwriting(Path path, String newName) throws IOException { - Path newPath = path.resolveSibling(newName); - for (int i = 2; Files.exists(newPath); i++) { - newPath = path.resolveSibling(newName + " " + i); - } - Files.move(path, newPath); - LOG.info("Renaming {} to {}", path, newPath.getFileName()); - } - - private void migrateLong(Path metadataDir, Path path) throws IOException { - String oldName = path.getFileName().toString(); - assert oldName.endsWith(LONG_FILENAME_EXT); - String oldNameBase = StringUtils.removeEnd(oldName, LONG_FILENAME_EXT); - - Matcher m = BASE32_PATTERN.matcher(oldNameBase); - if (m.find(0)) { - String oldNameBase32 = m.group(1); - String oldNameSuffix = oldNameBase.substring(m.end()); - String oldCanonicalName = oldNameBase32 + LONG_FILENAME_EXT; - - Path oldMetadataFile = metadataDir.resolve(oldCanonicalName.substring(0, 2)).resolve(oldCanonicalName.substring(2, 4)).resolve(oldCanonicalName); - if (!Files.isReadable(oldMetadataFile)) { - LOG.warn("Found uninflatable long file name. Expected: {}", oldMetadataFile); - return; - } - - String oldLongName = new String(Files.readAllBytes(oldMetadataFile), UTF_8); - if (oldLongName.endsWith(OLD_FOLDER_SUFFIX)) { - String newLongName = NEW_FOLDER_PREFIX + StringUtils.removeEnd(oldLongName, OLD_FOLDER_SUFFIX); - String newCanonicalBase32 = base32.encode(sha1.digest(newLongName.getBytes(UTF_8))); - String newCanonicalName = newCanonicalBase32 + LONG_FILENAME_EXT; - Path newMetadataFile = metadataDir.resolve(newCanonicalName.substring(0, 2)).resolve(newCanonicalName.substring(2, 4)).resolve(newCanonicalName); - String newName = newCanonicalBase32 + oldNameSuffix + LONG_FILENAME_EXT; - Path newPath = path.resolveSibling(newName); - Files.move(path, newPath); - Files.createDirectories(newMetadataFile.getParent()); - Files.write(newMetadataFile, newLongName.getBytes(UTF_8)); - LOG.info("Renaming {} to {}.", path, newName); - LOG.info("Creating {}.", newMetadataFile); - } - } - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion4to5.java b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion4to5.java deleted file mode 100644 index a1706dd8e..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion4to5.java +++ /dev/null @@ -1,167 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.ui.model.upgrade; - -import org.cryptomator.ui.fxapp.FxApplicationScoped; -import org.cryptomator.cryptolib.Cryptors; -import org.cryptomator.cryptolib.api.Cryptor; -import org.cryptomator.cryptolib.api.FileHeader; -import org.cryptomator.ui.l10n.Localization; -import org.cryptomator.common.vaults.Vault; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.file.FileVisitOption; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.StandardOpenOption; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.EnumSet; -import java.util.regex.Pattern; - -/** - * Contains the collective knowledge of all creatures who were alive during the development of vault format 3. - * This class uses no external classes from the crypto or shortening layer by purpose, so we don't need legacy code inside these. - */ -@FxApplicationScoped -class UpgradeVersion4to5 extends UpgradeStrategy { - - private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion4to5.class); - private static final Pattern LVL1_DIR_PATTERN = Pattern.compile("[A-Z2-7]{2}"); - private static final Pattern LVL2_DIR_PATTERN = Pattern.compile("[A-Z2-7]{30}"); - private static final Pattern BASE32_PATTERN = Pattern.compile("^([A-Z2-7]{8})*[A-Z2-7=]{8}"); - - @Inject - public UpgradeVersion4to5(Localization localization) { - super(Cryptors.version1(UpgradeStrategy.strongSecureRandom()), localization, 4, 5); - } - - @Override - public String getTitle(Vault vault) { - return localization.getString("upgrade.version4to5.title"); - } - - @Override - public String getMessage(Vault vault) { - return localization.getString("upgrade.version4to5.msg"); - } - - @Override - protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException { - Path dataDir = vault.getPath().resolve("d"); - if (!Files.isDirectory(dataDir)) { - return; // empty vault. no migration needed. - } - try { - Files.walkFileTree(dataDir, EnumSet.noneOf(FileVisitOption.class), 3, new SimpleFileVisitor() { - - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - if (dir.equals(dataDir)) { - // path/to/vault/d - return FileVisitResult.CONTINUE; - } else if (dir.getParent().equals(dataDir) && LVL1_DIR_PATTERN.matcher(dir.getFileName().toString()).matches()) { - // path/to/vault/d/AB - return FileVisitResult.CONTINUE; - } else if (dir.getParent().getParent().equals(dataDir) && LVL2_DIR_PATTERN.matcher(dir.getFileName().toString()).matches()) { - // path/to/vault/d/AB/CDEFGHIJKLMNOPQRSTUVWXYZ234567 - return FileVisitResult.CONTINUE; - } else { - LOG.info("Skipping irrelevant directory {}", dir); - return FileVisitResult.SKIP_SUBTREE; - } - } - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - if (BASE32_PATTERN.matcher(file.getFileName().toString()).find() && attrs.size() > cryptor.fileHeaderCryptor().headerSize()) { - migrate(file, attrs, cryptor); - } else { - LOG.info("Skipping irrelevant file {}.", file); - } - return FileVisitResult.CONTINUE; - } - - }); - } catch (IOException e) { - LOG.error("Migration failed.", e); - throw new UpgradeFailedException(localization.getString("upgrade.version4to5.err.io")); - } - LOG.info("Migration finished."); - } - - @SuppressWarnings("deprecation") - private void migrate(Path file, BasicFileAttributes attrs, Cryptor cryptor) throws IOException { - LOG.info("Starting migration of {}...", file); - try (FileChannel ch = FileChannel.open(file, StandardOpenOption.READ, StandardOpenOption.WRITE)) { - // read header: - ByteBuffer headerBuf = ByteBuffer.allocate(cryptor.fileHeaderCryptor().headerSize()); - ch.read(headerBuf); - headerBuf.flip(); - LOG.info("\tHeader read"); - FileHeader header = cryptor.fileHeaderCryptor().decryptHeader(headerBuf); - long cleartextSize = header.getFilesize(); - if (cleartextSize < 0) { - LOG.info("\tSkipping already migrated file"); - return; - } else if (cleartextSize > attrs.size()) { - LOG.warn("\tSkipping file with invalid file size {}/{}", cleartextSize, attrs.size()); - return; - } - int headerSize = cryptor.fileHeaderCryptor().headerSize(); - int ciphertextChunkSize = cryptor.fileContentCryptor().ciphertextChunkSize(); - int cleartextChunkSize = cryptor.fileContentCryptor().cleartextChunkSize(); - long newCiphertextSize = Cryptors.ciphertextSize(cleartextSize, cryptor); - long newEOF = headerSize + newCiphertextSize; - long newFullChunks = newCiphertextSize / ciphertextChunkSize; // int-truncation - long newAdditionalCiphertextBytes = newCiphertextSize % ciphertextChunkSize; - if (newAdditionalCiphertextBytes == 0) { - // (new) last block is already correct. just truncate: - LOG.info("\tMigrating cleartext size {}: Truncating to new ciphertext size: {}", cleartextSize, newEOF); - ch.truncate(newEOF); - LOG.info("\tFile truncated"); - } else { - // last block may contain padding and needs to be re-encrypted: - long lastChunkIdx = newFullChunks; - LOG.info("\tMigrating cleartext size {}: Re-encrypting chunk {}. New ciphertext size: {}", cleartextSize, lastChunkIdx, newEOF); - long beginOfLastChunk = headerSize + lastChunkIdx * ciphertextChunkSize; - assert beginOfLastChunk < newEOF; - int lastCleartextChunkLength = (int) (cleartextSize % cleartextChunkSize); - assert lastCleartextChunkLength < cleartextChunkSize; - assert lastCleartextChunkLength > 0; - ch.position(beginOfLastChunk); - ByteBuffer lastCiphertextChunk = ByteBuffer.allocate(ciphertextChunkSize); - int read = ch.read(lastCiphertextChunk); - if (read != -1) { - lastCiphertextChunk.flip(); - ByteBuffer lastCleartextChunk = cryptor.fileContentCryptor().decryptChunk(lastCiphertextChunk, lastChunkIdx, header, true); - lastCleartextChunk.position(0).limit(lastCleartextChunkLength); - assert lastCleartextChunk.remaining() == lastCleartextChunkLength; - ByteBuffer newLastChunkCiphertext = cryptor.fileContentCryptor().encryptChunk(lastCleartextChunk, lastChunkIdx, header); - ch.truncate(beginOfLastChunk); - ch.write(newLastChunkCiphertext); - } else { - LOG.error("\tReached EOF at position {}/{}", beginOfLastChunk, newEOF); - return; // must exit method before changing header! - } - LOG.info("\tReencrypted last block"); - } - header.setFilesize(-1l); - ByteBuffer newHeaderBuf = cryptor.fileHeaderCryptor().encryptHeader(header); - ch.position(0); - ch.write(newHeaderBuf); - LOG.info("\tUpdated header"); - } - LOG.info("Finished migration of {}.", file); - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion5toX.java b/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion5toX.java deleted file mode 100644 index 9c78d91ad..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/model/upgrade/UpgradeVersion5toX.java +++ /dev/null @@ -1,70 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2017 Skymatic UG (haftungsbeschränkt). - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the accompanying LICENSE file. - *******************************************************************************/ -package org.cryptomator.ui.model.upgrade; - -import org.cryptomator.ui.fxapp.FxApplicationScoped; -import org.cryptomator.cryptofs.migration.Migrators; -import org.cryptomator.cryptofs.migration.api.NoApplicableMigratorException; -import org.cryptomator.cryptolib.Cryptors; -import org.cryptomator.cryptolib.api.Cryptor; -import org.cryptomator.cryptolib.api.InvalidPassphraseException; -import org.cryptomator.ui.l10n.Localization; -import org.cryptomator.common.vaults.Vault; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.inject.Inject; -import java.io.IOException; - -@FxApplicationScoped -class UpgradeVersion5toX extends UpgradeStrategy { - - private static final Logger LOG = LoggerFactory.getLogger(UpgradeVersion5toX.class); - - @Inject - public UpgradeVersion5toX(Localization localization) { - super(Cryptors.version1(UpgradeStrategy.strongSecureRandom()), localization, 5, Integer.MAX_VALUE); - } - - @Override - public String getTitle(Vault vault) { - return localization.getString("upgrade.version5toX.title"); - } - - @Override - public String getMessage(Vault vault) { - return localization.getString("upgrade.version5toX.msg"); - } - - @Override - public void upgrade(Vault vault, CharSequence passphrase) throws UpgradeFailedException { - try { - Migrators.get().migrate(vault.getPath(), "masterkey.cryptomator", passphrase); - } catch (InvalidPassphraseException e) { - throw new UpgradeFailedException(localization.getString("unlock.errorMessage.wrongPassword")); - } catch (NoApplicableMigratorException | IOException e) { - LOG.warn("Upgrade failed.", e); - throw new UpgradeFailedException("Upgrade failed. Details in log message."); - } - } - - @Override - protected void upgrade(Vault vault, Cryptor cryptor) throws UpgradeFailedException { - // called by #upgrade(Vault, CharSequence), which got overwritten. - throw new AssertionError("Method can not be called."); - } - - @Override - public boolean isApplicable(Vault vault) { - try { - return Migrators.get().needsMigration(vault.getPath(), "masterkey.cryptomator"); - } catch (IOException e) { - LOG.warn("Could not determine, whether upgrade is applicable.", e); - return false; - } - } - -} diff --git a/main/ui/src/main/java/org/cryptomator/ui/quit/QuitController.java b/main/ui/src/main/java/org/cryptomator/ui/quit/QuitController.java index 7f6334eb2..897bce32c 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/quit/QuitController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/quit/QuitController.java @@ -65,6 +65,7 @@ public class QuitController implements FxController { lockAndQuitButton.setDisable(false); lockAndQuitButton.setContentDisplay(ContentDisplay.TEXT_ONLY); // TODO: show force lock or force quit scene (and DO NOT cancelQuit() here!) + // see https://github.com/cryptomator/cryptomator/blob/1.4.16/main/ui/src/main/java/org/cryptomator/ui/model/Vault.java#L151-L163 response.cancelQuit(); }); lockAllService.start(); diff --git a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java index 4e6bbada1..7c1221aaf 100644 --- a/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java +++ b/main/ui/src/main/java/org/cryptomator/ui/unlock/UnlockController.java @@ -36,7 +36,6 @@ import java.nio.file.DirectoryNotEmptyException; import java.nio.file.NotDirectoryException; import java.util.Arrays; import java.util.Optional; -import java.util.ResourceBundle; import java.util.concurrent.ExecutorService; @UnlockScoped @@ -49,7 +48,6 @@ public class UnlockController implements FxController { private final ExecutorService executor; private final ObjectBinding unlockButtonState; private final Optional keychainAccess; - private final ResourceBundle resourceBundle; private final Lazy successScene; private final ForgetPasswordComponent.Builder forgetPassword; private final BooleanProperty unlockButtonDisabled; @@ -57,13 +55,12 @@ public class UnlockController implements FxController { public CheckBox savePassword; @Inject - public UnlockController(@UnlockWindow Stage window, @UnlockWindow Vault vault, ExecutorService executor, Optional keychainAccess, ResourceBundle resourceBundle, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy successScene, ForgetPasswordComponent.Builder forgetPassword) { + public UnlockController(@UnlockWindow Stage window, @UnlockWindow Vault vault, ExecutorService executor, Optional keychainAccess, @FxmlScene(FxmlFile.UNLOCK_SUCCESS) Lazy successScene, ForgetPasswordComponent.Builder forgetPassword) { this.window = window; this.vault = vault; this.executor = executor; this.unlockButtonState = Bindings.createObjectBinding(this::getUnlockButtonState, vault.stateProperty()); this.keychainAccess = keychainAccess; - this.resourceBundle = resourceBundle; this.successScene = successScene; this.forgetPassword = forgetPassword; this.unlockButtonDisabled = new SimpleBooleanProperty(); @@ -126,7 +123,7 @@ public class UnlockController implements FxController { @FXML private void didClickSavePasswordCheckbox() { if (!savePassword.isSelected() && hasStoredPassword()) { - forgetPassword.vault(vault).build().showForgetPassword().thenAccept(forgotten -> savePassword.setSelected(!forgotten)); + forgetPassword.vault(vault).owner(window).build().showForgetPassword().thenAccept(forgotten -> savePassword.setSelected(!forgotten)); } } diff --git a/main/ui/src/main/java/org/cryptomator/ui/util/DialogBuilderUtil.java b/main/ui/src/main/java/org/cryptomator/ui/util/DialogBuilderUtil.java deleted file mode 100644 index 744c37cff..000000000 --- a/main/ui/src/main/java/org/cryptomator/ui/util/DialogBuilderUtil.java +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2016, 2017 Sebastian Stenzel and others. - * All rights reserved. - * This program and the accompanying materials are made available under the terms of the accompanying LICENSE file. - * - * Contributors: - * Jean-Noël Charon - initial API and implementation - *******************************************************************************/ -package org.cryptomator.ui.util; - -import javafx.scene.control.Alert; -import javafx.scene.control.Button; -import javafx.scene.control.ButtonType; -import javafx.scene.text.Text; - -public class DialogBuilderUtil { - - public DialogBuilderUtil() { - } - - public static Alert buildInformationDialog(String title, String header, String content, ButtonType defaultButton) { - return buildDialog(title, header, content, Alert.AlertType.INFORMATION, defaultButton); - } - - public static Alert buildWarningDialog(String title, String header, String content, ButtonType defaultButton) { - return buildDialog(title, header, content, Alert.AlertType.WARNING, defaultButton); - } - - public static Alert buildErrorDialog(String title, String header, String content, ButtonType defaultButton) { - return buildDialog(title, header, content, Alert.AlertType.ERROR, defaultButton); - } - - public static Alert buildConfirmationDialog(String title, String header, String content, ButtonType defaultButton) { - return buildDialog(title, header, content, Alert.AlertType.CONFIRMATION, defaultButton); - } - - public static Alert buildYesNoDialog(String title, String header, String content, ButtonType defaultButton) { - return buildDialog(title, header, content, Alert.AlertType.CONFIRMATION, defaultButton, ButtonType.YES, ButtonType.NO); - } - - public static Alert buildGracefulShutdownDialog(String title, String header, String content, ButtonType defaultButton, ButtonType... buttons) { - return buildDialog(title, header, content, Alert.AlertType.WARNING, defaultButton, buttons); - } - - private static Alert buildDialog(String title, String header, String content, Alert.AlertType type, ButtonType defaultButton, ButtonType... buttons) { - Text contentText = new Text(content); - contentText.setWrappingWidth(360.0); - - Alert alert = new Alert(type, content, buttons); - alert.setTitle(title); - alert.setHeaderText(header); - alert.getDialogPane().setContent(contentText); - - alert.getDialogPane().getButtonTypes().stream().forEach(buttonType -> { - Button btn = (Button) alert.getDialogPane().lookupButton(buttonType); - btn.setDefaultButton(buttonType.equals(defaultButton)); - }); - - return alert; - } -} diff --git a/main/ui/src/main/resources/css/linux_theme.css b/main/ui/src/main/resources/css/linux_theme.css deleted file mode 100644 index 91a7de6d8..000000000 --- a/main/ui/src/main/resources/css/linux_theme.css +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Copyright (c) 2015 Sebastian Stenzel - * This file is licensed under the terms of the MIT license. - * See the LICENSE.txt file for more info. - * - * Contributors: - * Sebastian Stenzel - initial API and implementation - * Jean-Noël Charon - implementation of the dialog css - * - */ - - .root { - -fx-font-family: 'Cantarell'; - -fx-font-smoothing-type: lcd; - -fx-font-size: 12px; - - COLOR_TEXT: #444; - COLOR_TEXT_LIGHT: #888; - COLOR_TEXT_DISABLED: #BBB; - COLOR_HYPERLINK: #0069D9; - COLOR_BORDER: #D3D3D3; - COLOR_BORDER_DARK: #AAA; - COLOR_BACKGROUND: #F0F0F0; - - COLOR_VGRAD_LIGHT: linear-gradient(to bottom, #FEFEFE 0%, #F5F5F5 100%); - COLOR_VGRAD_MEDIUM: linear-gradient(to bottom, #F5F5F5 0%, #F1F1F1 100%); - COLOR_VGRAD_DARK: linear-gradient(to bottom, #F1F1F1 0%, #E9E9E9 100%); - COLOR_HGRAD_LIGHT: linear-gradient(to right, #FEFEFE 0%, #F5F5F5 100%); - COLOR_HGRAD_MEDIUM: linear-gradient(to right, #F5F5F5 0%, #FEFEFE 100%); - COLOR_HGRAD_DARK: linear-gradient(to right, #F1F1F1 0%, #E9E9E9 100%); - - COLOR_CHART_GREEN: #28CA40; - COLOR_CHART_RED: #FD4943; - - - -fx-background-color: COLOR_BACKGROUND; - -fx-text-fill: COLOR_TEXT; -} - -/**************************************************************************** - * * - * Labels & Fonts * - * * - ****************************************************************************/ - -.label { - -fx-text-fill: COLOR_TEXT; -} - -.ionicons { - -fx-font-family: Ionicons; -} - -.fontawesome { - -fx-font-family: FontAwesome; -} -.caption-label { - -fx-font-size: 0.9em; -} - -/**************************************************************************** - * * - * Hyperlinks * - * * - ****************************************************************************/ - -.hyperlink { - -fx-cursor: hand; - -fx-text-fill: COLOR_HYPERLINK; -} - -.hyperlink:hover { - -fx-underline: true; -} - -/**************************************************************************** - * * - * Buttons * - * * - ****************************************************************************/ - -.button, -.toggle-button { - -fx-pref-height: 25px; - -fx-background-color: COLOR_BORDER, COLOR_VGRAD_LIGHT; - -fx-background-insets: 0, 1; - -fx-padding: 4px 12px 6px 12px; - -fx-text-fill: COLOR_TEXT; - -fx-alignment: CENTER; -} - -.button:hover, -.button:default:hover, -.toggle-button:hover { - -fx-background-color: COLOR_BORDER, COLOR_VGRAD_MEDIUM; -} - -.button:armed, -.button:default:armed, -.toggle-button:armed { - -fx-background-color: COLOR_BORDER_DARK, COLOR_VGRAD_DARK; -} - -.button:disabled, -.button:default:disabled, -.toggle-button:disabled { - -fx-background-color: COLOR_BORDER, COLOR_VGRAD_LIGHT; - -fx-text-fill: COLOR_TEXT_DISABLED; -} - -.button:focused, -.toggle-button:focused { - -fx-border-color: black; - -fx-border-insets: 2px; - -fx-border-style: dotted inside; -} - -/**************************************************************************** - * * - * Segmented Buttons * - * * - ****************************************************************************/ - -.segmented-button-bar .button, -.segmented-button-bar .toggle-button { - -fx-background-insets: 0, 1; -} - -.segmented-button-bar .button.last, -.segmented-button-bar .toggle-button.last { - -fx-background-insets: 0, 1 1 1 0; -} - -/**************************************************************************** - * * - * Text Fields * - * * - ****************************************************************************/ - -.text-input { - -fx-background-color: COLOR_BORDER, #FFF; - -fx-background-insets: 0, 1; - -fx-text-fill: COLOR_TEXT; - -fx-padding: 0.4em; - -fx-cursor: text; -} -.text-input:focused { - -fx-background-color: COLOR_BORDER_DARK, #FFF; -} -.text-input:disabled { - -fx-background-color: COLOR_BORDER, COLOR_BACKGROUND; - -fx-text-fill: COLOR_TEXT_DISABLED; -} - -/**************************************************************************** - * * - * Vault List * - * * - ****************************************************************************/ - -.list-view { - -fx-background-color: COLOR_BORDER, #FFF; - -fx-background-insets: 0, 1; - -fx-padding: 1; -} - -.list-view:focused { - -fx-border-style: dotted inside; -} - -.list-cell { - -fx-padding: 0.5em; - -fx-text-fill: COLOR_TEXT; -} - -.list-cell:selected { - -fx-background-color: COLOR_VGRAD_DARK; -} - -.list-cell .detail-label { - -fx-text-fill: COLOR_TEXT_LIGHT; - -fx-font-size: 0.8em; -} - -/******************************************************************************* - * * - * ScrollBar * - * * - ******************************************************************************/ -.scroll-bar:vertical { - -fx-background-color: COLOR_HGRAD_MEDIUM; - -fx-border-color: transparent transparent transparent COLOR_BORDER; - -fx-padding: 2px; -} -.scroll-bar > .thumb { - -fx-background-color: COLOR_BORDER, COLOR_HGRAD_LIGHT; - -fx-background-insets: 0, 1; - -fx-background-radius: 5; -} -.scroll-bar > .thumb:hover, -.scroll-bar > .thumb:pressed { - -fx-background-color: COLOR_BORDER, COLOR_HGRAD_DARK; -} -.scroll-bar:vertical > .increment-button, -.scroll-bar:vertical > .decrement-button { - -fx-padding: 0 5 0 5; -} - -/**************************************************************************** - * * - * Vault List Toolbar * - * * - ****************************************************************************/ - -.tool-bar.list-related-toolbar { - -fx-background-color: COLOR_BORDER, COLOR_VGRAD_LIGHT; - -fx-background-insets: 0, 0 1 1 1; - -fx-padding: 0; - -fx-spacing: 0; - -fx-alignment: CENTER_LEFT; -} - -.tool-bar.list-related-toolbar .spacer { - -fx-border-color: transparent COLOR_BORDER transparent transparent; - -fx-border-width: 1; -} - -.tool-bar.list-related-toolbar .toggle-button, -.tool-bar.list-related-toolbar .button { - -fx-pref-height: 30px; - -fx-font-size: 1.8em; - -fx-text-fill: COLOR_TEXT; - -fx-padding: 0.2em 0.8em 0.2em 0.8em; - -fx-background-color: transparent; - -fx-background-insets: 1; - -fx-border-color: transparent COLOR_BORDER transparent transparent; - -fx-border-width: 1; -} - -.tool-bar.list-related-toolbar .toggle-button:disabled, -.tool-bar.list-related-toolbar .button:disabled { - -fx-text-fill: COLOR_TEXT_DISABLED; -} - -.tool-bar.list-related-toolbar .toggle-button:hover, -.tool-bar.list-related-toolbar .button:hover { - -fx-background-color: COLOR_VGRAD_MEDIUM; -} - -.tool-bar.list-related-toolbar .toggle-button:armed, -.tool-bar.list-related-toolbar .toggle-button:selected, -.tool-bar.list-related-toolbar .button:armed, -.tool-bar.list-related-toolbar .button:selected { - -fx-background-color: COLOR_VGRAD_DARK; -} - -.tool-bar.list-related-toolbar .toggle-button:focused, -.tool-bar.list-related-toolbar .button:focused { - -fx-border-style: dotted inside; - -fx-border-color: black; -} - -/******************************************************************************* - * * - * PopupMenu * - * * - ******************************************************************************/ - -.context-menu { - -fx-font-size: 12px; - -fx-background-color: COLOR_BORDER, COLOR_BACKGROUND; - -fx-background-insets: 0, 1; - -fx-padding: 1; - -fx-effect: dropshadow(three-pass-box, rgba(0.0, 0.0, 0.0, 0.2), 3, 0, 0, 2); -} -.context-menu > .separator { - -fx-padding: 0.0em 0.333333em 0.0em 0.333333em; /* 0 4 0 4 */ -} -.menu-item { - -fx-background-color: transparent; - -fx-padding: 0.4em; -} -.menu-item > .graphic-container { - -fx-padding: 0 6px 0 0; -} -.menu-item > .label { - -fx-text-fill: COLOR_TEXT; -} -.menu-item:focused { - -fx-background-color: COLOR_VGRAD_DARK; -} -.menu-item:disabled > .label { - -fx-text-fill: COLOR_TEXT_DISABLED; -} - -/**************************************************************************** - * * - * Tooltip * - * * - ****************************************************************************/ - -.tooltip { - -fx-background-color: COLOR_BORDER_DARK, #FE9; - -fx-background-insets: 0, 1; - -fx-padding: 0.4em 0.3em 0.4em 0.3em; - -fx-font-size: 0.8em; -} - -/**************************************************************************** - * * - * Separator * - * * - ****************************************************************************/ - -.separator .line { - -fx-border-style: solid; - -fx-border-width: 1px; - -fx-background-color: null; -} - -.separator:horizontal .line { - -fx-border-color: COLOR_BORDER transparent transparent transparent; -} - -/**************************************************************************** - * * - * CheckBox * - * * - ****************************************************************************/ - -.check-box { - -fx-label-padding: 0 0 0 6px; - -fx-text-fill: COLOR_TEXT; -} -.check-box > .box { - -fx-padding: 3px; - -fx-background-color: COLOR_BORDER, #FFF; - -fx-background-insets: 0, 1; -} -.check-box > .box > .mark { - -fx-background-color: transparent; - -fx-padding: 4px; - -fx-shape: "M-1,4, L-1,5.5 L3.5,8.5 L9,0 L9,-1 L7,-1 L3,6 L1,4 Z"; -} -/* armed: */ -.check-box:armed > .box { - -fx-background-color: COLOR_BORDER_DARK, #FFF; -} -/* selected: */ -.check-box:selected > .box > .mark { - -fx-background-color: COLOR_TEXT; -} -/* focused */ -.check-box:focused { - -fx-border-style: dotted inside; -} -/* disabled: */ -.check-box:disabled > .box { - -fx-background-color: COLOR_BORDER, COLOR_BACKGROUND; -} -.check-box:disabled:selected > .box > .mark { - -fx-background-color: COLOR_TEXT_DISABLED; -} - -/******************************************************************************* - * * - * ChoiceBox * - * * - ******************************************************************************/ - -.choice-box { - -fx-background-color: COLOR_BORDER_DARK, COLOR_BACKGROUND; - -fx-background-insets: 0, 1; - -fx-background-radius: 0, 0; - -fx-padding: 0.1em 0.6em 0.1em 0.6em; - -fx-text-fill: COLOR_TEXT; -} - -.choice-box:focused { - -fx-border-style: dotted inside; -} - -.choice-box > .open-button > .arrow { - -fx-background-color: transparent, COLOR_TEXT; - -fx-background-insets: 0 0 -1 0, 0; - -fx-padding: 0.166667em 0.333333em 0.166667em 0.333333em; /* 2 4 2 4 */ - -fx-shape: "M 0 0 h 7 l -3.5 4 z"; -} - -.choice-box .context-menu { - -fx-background-color: COLOR_BORDER, #FFF; - -fx-background-insets: 0, 1; -} - -/**************************************************************************** - * * - * ProgressIndicator * - * * - ****************************************************************************/ - -.progress-indicator { - -fx-indeterminate-segment-count: 12; - -fx-spin-enabled: true; -} -.progress-indicator:indeterminate > .spinner { - -fx-padding: 0.083333em; -} -.progress-indicator:indeterminate .segment { - -fx-background-color: COLOR_BORDER_DARK; -} -.progress-indicator:indeterminate .segment0 { - -fx-shape:"M10,0C9.998,0,9.995,0,9.992,0C9.991,0,9.991,0,9.99,0C9.988,0,9.986,0,9.985,0S9.982,0,9.981,0S9.979,0,9.978,0S9.975,0,9.974,0S9.972,0,9.971,0C9.969,0,9.968,0,9.966,0H9.965c-0.007,0-0.014,0-0.02,0C9.944,0,9.944,0,9.944,0C9.941,0,9.939,0,9.937,0c-0.77,0.007-1.389,0.634-1.384,1.404C8.557,2.176,9.185,2.8,9.956,2.8c0.001,0,0.003,0,0.004,0H10c0.773-0.002,1.4-0.63,1.4-1.404c0-0.77-0.622-1.393-1.392-1.396C10.006,0,10.003,0,10,0L10,0z"; -} -.progress-indicator:indeterminate .segment1 { - -fx-shape:"M5.688,1.156c-0.236,0-0.476,0.06-0.696,0.187C4.98,1.349,4.969,1.356,4.958,1.363c0,0-0.001,0-0.001,0C4.955,1.364,4.954,1.365,4.952,1.366c-0.001,0-0.002,0.001-0.004,0.002c0,0,0,0-0.001,0C4.944,1.371,4.94,1.373,4.936,1.375c0,0,0,0-0.001,0C4.933,1.377,4.931,1.378,4.929,1.38C4.267,1.772,4.046,2.624,4.438,3.288c0.261,0.444,0.73,0.692,1.212,0.692c0.24,0,0.484-0.062,0.706-0.192l0.034-0.02C7.058,3.378,7.283,2.52,6.896,1.851C6.636,1.405,6.168,1.156,5.688,1.156L5.688,1.156z"; -} -.progress-indicator:indeterminate .segment2 { - -fx-shape:"M2.534,4.326c-0.482,0-0.951,0.25-1.209,0.697C1.323,5.027,1.321,5.029,1.32,5.031l0,0C1.319,5.033,1.318,5.034,1.317,5.036S1.315,5.039,1.314,5.04c0,0.001,0,0.002-0.001,0.002C1.312,5.044,1.311,5.046,1.31,5.048c0,0,0,0,0,0.001C1.309,5.051,1.308,5.053,1.307,5.055C1.302,5.063,1.297,5.071,1.292,5.079l0,0C1.291,5.082,1.29,5.084,1.288,5.087c-0.376,0.67-0.141,1.519,0.529,1.898c0.218,0.123,0.456,0.182,0.69,0.182c0.488,0,0.963-0.255,1.222-0.708l0.02-0.035c0.383-0.671,0.149-1.527-0.521-1.912C3.008,4.386,2.769,4.326,2.534,4.326L2.534,4.326z"; -} -.progress-indicator:indeterminate .segment3 { - -fx-shape:"M1.396,8.648c-0.002,0-0.005,0-0.008,0C0.619,8.652-0.001,9.278,0,10.047c0,0.002,0,0.006,0,0.008l0,0c0,0.019,0,0.037,0,0.056c0,0.001,0,0.002,0,0.003s0,0.003,0,0.004c0.01,0.765,0.632,1.378,1.396,1.378c0.005,0,0.01,0,0.015,0c0.773-0.009,1.395-0.642,1.389-1.415v-0.04C2.794,9.27,2.166,8.648,1.396,8.648L1.396,8.648z"; -} -.progress-indicator:indeterminate .segment4 { - -fx-shape:"M2.579,12.955c-0.242,0-0.487,0.062-0.71,0.194c-0.664,0.391-0.885,1.242-0.499,1.906c0.013,0.021,0.025,0.043,0.038,0.063c0.262,0.436,0.724,0.678,1.197,0.678c0.243,0,0.49-0.063,0.714-0.197c0.665-0.396,0.883-1.257,0.489-1.922l-0.02-0.034C3.526,13.201,3.059,12.955,2.579,12.955L2.579,12.955z"; -} -.progress-indicator:indeterminate .segment5 { - -fx-shape:"M5.772,16.09c-0.489,0-0.965,0.257-1.223,0.712c-0.38,0.671-0.146,1.52,0.523,1.901c0.002,0.001,0.004,0.002,0.007,0.004h0c0.004,0.002,0.008,0.004,0.012,0.007c0,0,0,0,0.001,0c0.001,0.001,0.002,0.002,0.004,0.002c0.001,0.001,0.003,0.002,0.004,0.003c0,0,0.001,0,0.001,0.001c0.012,0.006,0.023,0.013,0.035,0.019c0.214,0.119,0.446,0.176,0.675,0.176c0.489,0,0.963-0.258,1.22-0.716c0.377-0.675,0.137-1.529-0.537-1.908l-0.035-0.02C6.242,16.149,6.006,16.09,5.772,16.09L5.772,16.09z"; -} -.progress-indicator:indeterminate .segment6 { - -fx-shape:"M10.14,17.198c-0.006,0-0.013,0-0.02,0h-0.039c-0.773,0.011-1.394,0.646-1.385,1.419c0.008,0.767,0.631,1.382,1.396,1.382c0.003,0,0.006,0,0.009-0.001c0.024,0,0.051,0,0.075-0.001c0.769-0.016,1.38-0.648,1.367-1.418C11.53,17.813,10.904,17.198,10.14,17.198L10.14,17.198z"; -} -.progress-indicator:indeterminate .segment7 { - -fx-shape:"M14.433,15.97c-0.245,0-0.494,0.064-0.72,0.2l-0.034,0.021c-0.663,0.397-0.88,1.258-0.483,1.922c0.261,0.439,0.725,0.683,1.2,0.683c0.24,0,0.484-0.062,0.707-0.194c0.022-0.013,0.044-0.025,0.065-0.039c0.656-0.399,0.866-1.254,0.469-1.913C15.373,16.212,14.909,15.97,14.433,15.97L14.433,15.97z"; -} -.progress-indicator:indeterminate .segment8 { - -fx-shape:"M17.539,12.748c-0.493,0-0.973,0.261-1.229,0.723l-0.02,0.034c-0.376,0.676-0.133,1.53,0.542,1.907c0.216,0.121,0.45,0.178,0.681,0.178c0.487,0,0.96-0.256,1.217-0.71c0.003-0.006,0.007-0.012,0.01-0.019c0.007-0.013,0.015-0.025,0.021-0.038l0,0c0.002-0.003,0.003-0.005,0.004-0.008c0.369-0.675,0.124-1.521-0.55-1.893C18.001,12.805,17.769,12.748,17.539,12.748L17.539,12.748z"; -} -.progress-indicator:indeterminate .segment9 { - -fx-shape:"M18.603,8.408c-0.011,0-0.021,0-0.031,0c-0.773,0.018-1.388,0.657-1.373,1.431l0.001,0.04c0.015,0.765,0.641,1.377,1.403,1.377c0.008,0,0.016,0,0.023,0c0.77-0.013,1.383-0.646,1.373-1.414c0-0.003,0-0.006,0-0.009l0,0c-0.001-0.019-0.001-0.037-0.001-0.055c0-0.001,0-0.001-0.001-0.002c0-0.002,0-0.004,0-0.006C19.979,9.012,19.358,8.408,18.603,8.408L18.603,8.408z"; -} -.progress-indicator:indeterminate .segment10 { - -fx-shape:"M17.345,4.121c-0.248,0-0.5,0.066-0.728,0.205c-0.659,0.403-0.869,1.266-0.468,1.927l0.021,0.034c0.265,0.435,0.728,0.675,1.202,0.675c0.247,0,0.497-0.065,0.724-0.202c0.659-0.397,0.871-1.252,0.477-1.912c-0.007-0.011-0.013-0.021-0.02-0.032c-0.001-0.002-0.002-0.003-0.002-0.004c-0.001,0-0.001-0.001-0.001-0.002c-0.004-0.005-0.008-0.011-0.011-0.017c0-0.001,0-0.001-0.001-0.001c-0.001-0.002-0.002-0.004-0.004-0.007C18.271,4.358,17.813,4.121,17.345,4.121L17.345,4.121z"; -} -.progress-indicator:indeterminate .segment11 { - -fx-shape:"M14.104,1.039c-0.494,0-0.974,0.264-1.227,0.729c-0.37,0.679-0.12,1.531,0.559,1.903l0.034,0.019c0.214,0.117,0.445,0.173,0.673,0.173c0.495,0,0.976-0.262,1.231-0.726c0.371-0.674,0.129-1.519-0.542-1.894c-0.012-0.006-0.024-0.013-0.036-0.02c-0.007-0.004-0.014-0.008-0.021-0.012c-0.003-0.001-0.006-0.003-0.009-0.005C14.556,1.094,14.329,1.039,14.104,1.039L14.104,1.039z"; -} - -/***************************************************************************** - * * - * I/O Chart * - * * - *****************************************************************************/ - -.chart { - -fx-padding: 5px; -} - -/* legend */ - -.chart-legend { - -fx-text-fill: COLOR_TEXT; - -fx-background-color: transparent; - -fx-padding: 0.4em; -} -.chart-line-symbol { - -fx-background-radius: 5px; - -fx-padding: 5px; -} -.default-color0.chart-line-symbol { -fx-background-color: COLOR_CHART_GREEN; } -.default-color1.chart-line-symbol { -fx-background-color: COLOR_CHART_RED; } - -/* axis */ - -.axis { - -fx-tick-label-font-size: 0.8em; - -fx-tick-label-fill: COLOR_TEXT; -} -.axis-label { - -fx-text-fill: COLOR_TEXT; - -fx-padding: 0 0 0.8em 0; -} -.axis:left { - -fx-border-color: transparent COLOR_BORDER transparent transparent; -} -.axis-tick-mark, -.axis-minor-tick-mark { - -fx-fill: null; - -fx-stroke: #CCC; -} - -/* content */ - -.chart-content { - -fx-padding: 10px; -} -.chart-horizontal-grid-lines { - -fx-stroke: COLOR_BORDER; -} -.chart-alternative-column-fill { - -fx-fill: null; - -fx-stroke: null; -} -.chart-alternative-row-fill { - -fx-fill: null; - -fx-stroke: null; -} -.chart-vertical-zero-line, -.chart-horizontal-zero-line { - -fx-stroke: COLOR_BORDER; -} -.chart-series-line { - -fx-stroke-width: 2px; -} -.default-color0.chart-series-line { -fx-stroke: COLOR_CHART_GREEN; } -.default-color1.chart-series-line { -fx-stroke: COLOR_CHART_RED; } - -/******************************************************************************* - * * - * Dialog * - * * - ******************************************************************************/ - -.dialog-pane { - -fx-background-color: COLOR_BACKGROUND; - -fx-padding: 20px 20px 20px 96px; - - -fx-background-image: url("/img/dialog-appicon.png"); - -fx-background-repeat: no-repeat; - -fx-background-position: 20px 20px; -} - -/* HEADER */ -.dialog-pane:header .header-panel { - -fx-padding: 0 0 12px 0; -} - -/* TITLE */ -.dialog-pane:header .header-panel .label { - -fx-font-weight: bold; - -fx-wrap-text: true; - -fx-font-size: 14px; -} - -/* CONTENT LABEL */ -.dialog-pane > .content { - -fx-alignment: top-left; - -fx-wrap-text: true; - -fx-font-size: 12px; -} - -/* BUTTONS */ -.dialog-pane > .button-bar > .container { - -fx-padding: 20px 0 0 0; -} - -.alert.confirmation.dialog-pane, -.text-input-dialog.dialog-pane, -.choice-dialog.dialog-pane { - -fx-padding: 20px 20px 20px 80px; - -fx-background-image: url("/img/dialog-confirm.png"); -} - -.alert.information.dialog-pane { - -fx-padding: 20px 20px 20px 80px; - -fx-background-image: url("/img/dialog-information.png"); -} - -.alert.error.dialog-pane { - -fx-padding: 20px 20px 20px 80px; - -fx-background-image: url("/img/dialog-error.png"); -} - -.alert.warning.dialog-pane { - -fx-padding: 20px 20px 20px 80px; - -fx-background-image: url("/img/dialog-warning.png"); -} \ No newline at end of file diff --git a/main/ui/src/main/resources/css/mac_theme.css b/main/ui/src/main/resources/css/mac_theme.css deleted file mode 100644 index 8d7004f37..000000000 --- a/main/ui/src/main/resources/css/mac_theme.css +++ /dev/null @@ -1,656 +0,0 @@ -/* - * Copyright (c) 2015 Sebastian Stenzel - * This file is licensed under the terms of the MIT license. - * See the LICENSE.txt file for more info. - * - * Contributors: - * Sebastian Stenzel - initial API and implementation - * Jean-Noël Charon - implementation of the dialog css - * - */ - -.root { - -fx-font-family: 'lucida-grande', sans-serif; - -fx-font-smoothing-type: lcd; - -fx-font-size: 13px; - - COLOR_TEXT: #000; - COLOR_TEXT_LIGHT: #888; - COLOR_TEXT_DISABLED: #7B7B7B; - COLOR_HYPERLINK: #0069D9; - COLOR_BORDER: #C8C8C8; - COLOR_BORDER_DARK: #B5B5B5; - COLOR_BACKGROUND: #ECECEC; - - COLOR_HGRAD_BTN_BORDER: linear-gradient(to bottom, #C1C1C1 0%, #A6A6A6 100%); - COLOR_HGRAD_BTN_BORDER_DIS: linear-gradient(to bottom, #D2D2D2 0%, #C4C4C4 100%); - COLOR_HGRAD_BTN_DEF_BORDER: linear-gradient(to bottom, #4AA0F9 0%, #045FFF 100%); - COLOR_HGRAD_BTN_DEF_BACKGROUND: linear-gradient(to bottom, #69B2FA 0%, #0D81FF 100%); - COLOR_HGRAD_BTN_ARMED_BORDER: linear-gradient(to bottom, #237FFE 0%, #023FDD 100%); - COLOR_HGRAD_BTN_ARMED_BACKGROUND: linear-gradient(to bottom, #4A97FD 0%, #0867E4 100%); - - COLOR_CHART_GREEN: #28CA40; - COLOR_CHART_RED: #FD4943; - - -fx-background-color: COLOR_BACKGROUND; - -fx-text-fill: COLOR_TEXT; -} - -/**************************************************************************** - * * - * Labels & Fonts * - * * - ****************************************************************************/ - -.label { - -fx-text-fill: COLOR_TEXT; -} - -.ionicons { - -fx-font-family: Ionicons; -} - -.fontawesome { - -fx-font-family: FontAwesome; -} - -.caption-label { - -fx-font-size: 0.9em; -} - -/**************************************************************************** - * * - * Hyperlinks * - * * - ****************************************************************************/ - -.hyperlink { - -fx-cursor: hand; - -fx-text-fill: COLOR_HYPERLINK; -} - -.hyperlink:hover { - -fx-underline: true; -} - -/**************************************************************************** - * * - * Buttons * - * * - ****************************************************************************/ - -.button, -.toggle-button { - -fx-pref-height: 21px; - -fx-background-color: COLOR_HGRAD_BTN_BORDER, #FFF; - -fx-background-insets: 0, 1; - -fx-background-radius: 4; - -fx-padding: 2px 12px 3px 12px; - -fx-text-fill: COLOR_TEXT; - -fx-alignment: CENTER; - -fx-focus-traversable: false; - -fx-effect: dropshadow(one-pass-box, #DCDCDC, 0.0, 0.0, 0.0, 1.0); -} - -.root.active-window .button:default { - -fx-background-color: COLOR_HGRAD_BTN_DEF_BORDER, COLOR_HGRAD_BTN_DEF_BACKGROUND; - -fx-text-fill: #FFF; -} - -.button:disabled, -.button:default:disabled, -.toggle-button:disabled, -.root.active-window .button:default:disabled { - -fx-background-color: COLOR_HGRAD_BTN_BORDER_DIS, #F2F2F2; - -fx-background-insets: 0, 1; - -fx-text-fill: COLOR_TEXT_DISABLED; - -fx-effect: dropshadow(one-pass-box, #E0E0E0, 0.0, 0.0, 0.0, 0.5); -} - -.button:armed, -.root.active-window .button:default:armed { - -fx-background-color: COLOR_HGRAD_BTN_ARMED_BORDER, COLOR_HGRAD_BTN_ARMED_BACKGROUND; - -fx-text-fill: #FFF; -} - -.toggle-button:armed, -.toggle-button:selected { - -fx-background-color: linear-gradient(to bottom, #C0C0C0 0%, #ADADAD 100%); -} - -/**************************************************************************** - * * - * Segmented Buttons * - * * - ****************************************************************************/ - -.segmented-button-bar .button, -.segmented-button-bar .toggle-button { - -fx-background-radius: 0; - -fx-background-insets: 0, 1 1 1 0; -} - -.segmented-button-bar .button.first, -.segmented-button-bar .toggle-button.first { - -fx-background-radius: 4 0 0 4; - -fx-background-insets: 0, 1; -} - -.segmented-button-bar .button.last, -.segmented-button-bar .toggle-button.last { - -fx-background-radius: 0 4 4 0; -} - -/**************************************************************************** - * * - * Text Fields * - * * - ****************************************************************************/ - -.text-input { - -fx-text-fill: COLOR_TEXT; - -fx-highlight-fill: derive(#FFF,-20%); - -fx-highlight-text-fill: COLOR_TEXT; - -fx-prompt-text-fill: derive(#FFF,-30%); - -fx-border-color: COLOR_BORDER_DARK; - -fx-border-width: 1px; - -fx-background-color: #FFFFFF; - -fx-background-insets: 0; - -fx-background-radius: 0; - -fx-cursor: text; - -fx-padding: 2px; -} -.text-input:focused { - -fx-highlight-fill: #B2D7FF; - -fx-border-color: #78A6D7; - -fx-border-width: 1px; - -fx-background-color: #8FBDEE, #FFFFFF; - -fx-background-insets: -3, 0; - -fx-background-radius: 3, 0; - -fx-prompt-text-fill: transparent; -} -.text-input:disabled { - -fx-text-fill: COLOR_TEXT_DISABLED; - -fx-border-color: #CDCDCD; -} - -/**************************************************************************** - * * - * Vault List * - * * - ****************************************************************************/ - -.list-view { - -fx-background-color: COLOR_BORDER, #FFF; - -fx-background-insets: 0, 1; - -fx-padding: 1; -} - -.list-view > .virtual-flow > .scroll-bar:vertical { - -fx-background-insets: 0, 0 0 0 1; - -fx-padding: -1 -1 -1 0; -} -.list-view > .virtual-flow > .scroll-bar:horizontal { - -fx-background-insets: 0, 1 0 0 0; - -fx-padding: 0 -1 -1 -1; -} -.list-view > .virtual-flow > .corner { - -fx-background-color: COLOR_BORDER, #FFF; - -fx-background-insets: 0, 1 0 0 1; -} -.list-cell { - -fx-background-color: #FFF; - -fx-padding: 6px; - -fx-text-fill: COLOR_TEXT; - -fx-opacity: 1; -} - -.root.active-window .list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:focused, -.root.active-window .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected, -.root.active-window .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected:hover { - -fx-background-color: #0069D9; - -fx-text-fill: #FFF; -} - -.root.inactive-window .list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:focused, -.root.inactive-window .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected, -.root.inactive-window .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected:hover { - -fx-background-color: #DCDCDC; - -fx-text-fill: #FFF; -} - -.list-cell .detail-label { - -fx-text-fill: COLOR_TEXT_LIGHT; - -fx-font-size: 0.8em; -} - -.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:focused .detail-label, -.list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected .detail-label, -.list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected:hover .detail-label{ - -fx-text-fill: #FFF; -} - -/******************************************************************************* - * * - * ScrollBar * - * * - ******************************************************************************/ -.scroll-bar:horizontal, -.scroll-bar:vertical { - -fx-background-color: #E8E8E8, #FAFAFA; -} - -.scroll-bar:disabled { - -fx-opacity: 0.6; -} -.scroll-bar > .thumb { - -fx-background-color: #C1C1C1; - -fx-background-insets: 2px; - -fx-background-radius: 4px; -} -.scroll-bar > .thumb:hover { - -fx-background-color: #7D7D7D; -} - -.scroll-bar > .increment-button, -.scroll-bar > .decrement-button { - -fx-background-color: transparent; - -fx-color: transparent; -} - -.scroll-bar:horizontal > .increment-button, -.scroll-bar:horizontal > .decrement-button { - -fx-padding: 6 0 6 0; -} - -.scroll-bar:vertical > .increment-button, -.scroll-bar:vertical > .decrement-button { - -fx-padding: 0 6 0 6; -} - -/**************************************************************************** - * * - * Vault List Toolbar * - * * - ****************************************************************************/ - -.tool-bar.list-related-toolbar { - -fx-font-size: 1.4em; - -fx-background-color: COLOR_BORDER, #F7F7F7; - -fx-background-insets: 0, 0 1 1 1; - -fx-padding: 0; - -fx-spacing: 0; - -fx-alignment: CENTER_LEFT; -} - -.tool-bar.list-related-toolbar .spacer { - -fx-border-color: transparent COLOR_BORDER transparent transparent; - -fx-border-width: 1; -} - -.tool-bar.list-related-toolbar .toggle-button, -.tool-bar.list-related-toolbar .button { - -fx-pref-height: 25px; - -fx-text-fill: COLOR_TEXT; - -fx-background-color: transparent; - -fx-padding: 0.1em 0.6em 0.1em 0.6em; - -fx-background-insets: 0; - -fx-background-radius: 0; - -fx-border-color: transparent COLOR_BORDER transparent transparent; - -fx-border-width: 1; -} - -.tool-bar.list-related-toolbar .toggle-button:disabled, -.tool-bar.list-related-toolbar .button:disabled { - -fx-text-fill: COLOR_TEXT_DISABLED; -} - -.tool-bar.list-related-toolbar .toggle-button:armed, -.tool-bar.list-related-toolbar .toggle-button:selected, -.tool-bar.list-related-toolbar .button:armed, -.tool-bar.list-related-toolbar .button:selected { - -fx-background-color: linear-gradient(to bottom, #C0C0C0 0%, #ADADAD 100%); -} - -/******************************************************************************* - * * - * PopupMenu * - * * - ******************************************************************************/ - -.context-menu { - -fx-font-size: 13px; - -fx-background-color: rgba(255.0, 255.0, 255.0, 0.9); - -fx-background-insets: 0; - -fx-background-radius: 4.0; - -fx-padding: 4px 0 4px 0; - -fx-effect: dropshadow(three-pass-box, rgba(0.0,0.0,0.0,0.6), 8.0, 0.0, 0.0, 0.0 ); -} -.context-menu > .separator { - -fx-padding: 0.0em 0.333333em 0.0em 0.333333em; /* 0 4 0 4 */ -} -.menu-item { - -fx-background-color: transparent; - -fx-background-insets:0.0; - -fx-padding:0.2em 1em 0.2em 1em; - -fx-border-width: 0.0 0.0 0.0 0.0; - -fx-border-color:transparent; -} -.menu-item > .graphic-container { - -fx-padding: 1px 6px 0 0; -} -.menu-item > .label { - -fx-padding: 0em 0.5em 0em 0em; - -fx-text-fill: COLOR_TEXT; -} -.menu-item:disabled > .label { - -fx-opacity: 0.6; -} -.menu-item:focused { - -fx-background: #B2D7FF; - -fx-background-color: #2283FB; - -fx-text-fill: #FFF; -} -.menu-item:focused > .label { - -fx-text-fill: white; -} -.menu-item:disabled { - -fx-opacity: 0.6; -} - -/******************************************************************************* - * * - * Tooltip * - * * - ******************************************************************************/ - -.tooltip { - -fx-background-color: COLOR_BORDER, COLOR_BACKGROUND; - -fx-padding: 0.2em 0.4em 0.2em 0.4em; - -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.5), 2, 0, 0, 0); - -fx-font-size: 0.8em; -} - -/**************************************************************************** - * * - * Separator * - * * - ****************************************************************************/ - -.separator .line { - -fx-border-style: solid; - -fx-border-width: 1px; - -fx-background-color: null; -} - -.separator:horizontal .line { - -fx-border-color: COLOR_BORDER transparent transparent transparent; -} - -/******************************************************************************* - * * - * CheckBox * - * * - ******************************************************************************/ - -.check-box { - -fx-label-padding: 0 0 0 6px; - -fx-text-fill: COLOR_TEXT; -} -.check-box > .box { - -fx-padding: 3px; - -fx-background-color: linear-gradient(to bottom, #A5A5A5 0%, #B8B8B8 100%), #F3F3F3, #FFFFFF; - -fx-background-radius: 2.5, 2.5, 2.5; - -fx-background-insets: 0, 1, 2 1 1 1; -} -.check-box > .box > .mark { - -fx-background-color: transparent; - -fx-padding: 4px; - -fx-shape: "M-1,4, L-1,5.5 L3.5,8.5 L9,0 L9,-1 L7,-1 L3,6 L1,4 Z"; -} -/* selected: */ -.root.active-window .check-box:selected > .box { - -fx-background-color: #2C90FC, #3B99FC; - -fx-background-insets: 0, 1; -} -.root.active-window .check-box:selected > .box > .mark { - -fx-background-color: white; -} -.root.inactive-window .check-box:selected > .box > .mark { - -fx-background-color: #444444; -} -/* disabled: */ -.check-box:disabled { - -fx-text-fill: COLOR_TEXT_DISABLED; -} -.root.active-window .check-box:disabled > .box, -.root.active-window .check-box:disabled:selected > .box, -.root.inactive-window .check-box:disabled > .box, -.root.inactive-window .check-box:disabled:selected > .box { - -fx-background-color: linear-gradient(to bottom, #BBBBBB 0%, #C2C2C2 100%), #E9E9E9, #F1F1F1; -} -.root.active-window .check-box:disabled:selected > .box > .mark, -.root.inactive-window .check-box:disabled:selected > .box > .mark { - -fx-background-color: COLOR_TEXT_DISABLED; -} - -/******************************************************************************* - * * - * ChoiceBox * - * * - ******************************************************************************/ - -.choice-box { - -fx-background-color: COLOR_HGRAD_BTN_BORDER, #FFFFFF; - -fx-background-insets: 0, 1; - -fx-background-radius: 4; - -fx-padding: 0 0 0 0.8em; - -fx-text-fill: COLOR_TEXT; - -fx-alignment: CENTER; - -fx-effect: dropshadow(one-pass-box, #DCDCDC, 0.0, 0.0, 0.0, 1.0); -} - -.choice-box > .open-button { - -fx-background-insets: 0, 1 1 1 0; - -fx-background-radius: 0 4 4 0; - -fx-padding: 4 5 4 4; -} -.root.active-window .choice-box > .open-button { - -fx-background-color: COLOR_HGRAD_BTN_DEF_BORDER, COLOR_HGRAD_BTN_DEF_BACKGROUND; -} -.root.inactive-window .choice-box > .open-button { - -fx-background-color: COLOR_HGRAD_BTN_BORDER, #FFFFFF; -} - -.choice-box > .open-button > .arrow { - -fx-shape: "M 0 5 L 4 0 L 8 5 L 6 5 L 4 2 L 2 5 Z M 0 8 L 4 13 L 8 8 L 6 8 L 4 11 L 2 8 Z"; - -fx-scale-shape: true; - -fx-min-width: 9px; - -fx-min-height: 13px; -} -.root.active-window .choice-box > .open-button > .arrow { - -fx-background-color: #FFFFFF; -} -.root.inactive-window .choice-box > .open-button > .arrow { - -fx-background-color: #444444; -} - -.choice-box .context-menu { - -fx-background-color: COLOR_BORDER, #FFF; - -fx-background-insets: 0, 1; -} - -/**************************************************************************** - * * - * ProgressIndicator * - * Derived from aquafx-project.com, (C) Claudine Zillmann, see NOTICE.md * - * * - ****************************************************************************/ - -.progress-indicator { - -fx-indeterminate-segment-count: 12; - -fx-spin-enabled: true; -} -.progress-indicator:indeterminate > .spinner { - -fx-padding: 0.083333em; -} -.progress-indicator:indeterminate .segment { - -fx-background-color: rgb(95.0, 95.0, 98.0), rgb(122.0, 122.0, 125.0); - -fx-background-insets:0.0, 0.5; -} -.progress-indicator:indeterminate .segment0 { - -fx-shape:"m 12.007729,4.9541827 c -0.49762,0.7596865 0.893181,1.6216808 1.327833,0.7666252 L 15.456199,2.0477574 C 15.942094,1.2061627 14.61426,0.43953765 14.128365,1.2811324 z"; -} -.progress-indicator:indeterminate .segment1 { - -fx-shape:"m 9.2224559,4.62535 c -0.051108,0.9067177 1.5843581,0.957826 1.5332501,0 l 0,-4.24127319 c 0,-0.9717899 -1.5332501,-0.9717899 -1.5332501,0 z"; -} -.progress-indicator:indeterminate .segment2 { - -fx-shape:"M 8.0465401,4.9030617 C 8.5441601,5.6627485 7.1533584,6.5247425 6.7187068,5.6696872 L 4.5980702,1.9966363 C 4.1121752,1.1550418 5.4400085,0.38841683 5.9259035,1.2300114 z"; -} -.progress-indicator:indeterminate .segment3 { - -fx-shape:"M 5.7330066,6.5305598 C 6.5579512,6.9103162 5.8366865,8.3790371 5.0144939,7.8850315 L 1.2677551,5.8974832 C 0.409277,5.4420823 1.1277888,4.0876101 1.9862674,4.5430105 z"; -} -.progress-indicator:indeterminate .segment4 { - -fx-shape:"m 0.42171041,9.2083842 c -0.90671825,-0.051108 -0.95782608,1.5843588 0,1.5332498 l 4.24127319,0 c 0.9717899,0 0.9717899,-1.5332498 0,-1.5332498 z"; -} -.progress-indicator:indeterminate .segment5 { - -fx-shape:"M 5.7330066,13.443113 C 6.5579512,13.063356 5.8366865,11.594635 5.0144939,12.088641 L 1.2677551,14.076189 C 0.409277,14.53159 1.1277888,15.886062 1.9862674,15.430662 z"; -} -.progress-indicator:indeterminate .segment6 { - -fx-shape:"M 8.0465401,15.070611 C 8.5441601,14.310924 7.1533584,13.44893 6.7187068,14.303985 l -2.1206366,3.673051 c -0.485895,0.841595 0.8419383,1.60822 1.3278333,0.766625 z"; -} -.progress-indicator:indeterminate .segment7 { - -fx-shape:"m 9.2224559,19.539943 c -0.051108,0.906718 1.5843581,0.957826 1.5332501,0 l 0,-4.241273 c 0,-0.97179 -1.5332501,-0.97179 -1.5332501,0 z"; -} -.progress-indicator:indeterminate .segment8 { - -fx-shape:"m 12.10997,15.070611 c -0.49762,-0.759687 0.893182,-1.621681 1.327834,-0.766626 l 2.120636,3.673051 c 0.485895,0.841595 -0.841938,1.60822 -1.327833,0.766625 z"; -} -.progress-indicator:indeterminate .segment9 { - -fx-shape:"m 14.423504,13.443113 c -0.824945,-0.379757 -0.10368,-1.848478 0.718512,-1.354472 l 3.746739,1.987548 c 0.858478,0.455401 0.139967,1.809873 -0.718512,1.354473 z"; -} -.progress-indicator:indeterminate .segment10 { - -fx-shape:"m 15.372451,9.2445322 c -0.906719,-0.051108 -0.957826,1.5843588 0,1.5332498 l 4.241273,0 c 0.97179,0 0.97179,-1.5332498 0,-1.5332498 z"; -} -.progress-indicator:indeterminate .segment11 { - -fx-shape:"m 14.321262,6.5816808 c -0.824944,0.3797564 -0.10368,1.8484772 0.718513,1.3544717 L 18.786514,5.9486042 C 19.644992,5.4932031 18.92648,4.1387308 18.068001,4.5941315 z"; -} - -/***************************************************************************** - * * - * I/O Chart * - * * - *****************************************************************************/ - - .chart { - -fx-padding: 5px; -} - -/* legend */ - -.chart-legend { - -fx-text-fill: COLOR_TEXT; - -fx-background-color: transparent; - -fx-padding: 0.4em; -} -.chart-line-symbol { - -fx-background-color: #000, #FFF; - -fx-background-insets: 0, 2; - -fx-background-radius: 5px; - -fx-padding: 5px; -} -.default-color0.chart-line-symbol { -fx-background-color: COLOR_CHART_GREEN, #FFF; } -.default-color1.chart-line-symbol { -fx-background-color: COLOR_CHART_RED, #FFF; } - -/* axis */ - -.axis { - -fx-tick-label-font-size: 0.8em; - -fx-tick-label-fill: COLOR_TEXT; -} -.axis-label { - -fx-text-fill: COLOR_TEXT; - -fx-padding: 0 0 0.8em 0; -} -.axis:left { - -fx-border-color: transparent COLOR_BORDER transparent transparent; -} -.axis-tick-mark, -.axis-minor-tick-mark { - -fx-fill: null; - -fx-stroke: #CCC; -} - -/* content */ - -.chart-content { - -fx-padding: 10px; -} -.chart-horizontal-grid-lines { - -fx-stroke: COLOR_BORDER; -} -.chart-alternative-column-fill { - -fx-fill: null; - -fx-stroke: null; -} -.chart-alternative-row-fill { - -fx-fill: null; - -fx-stroke: null; -} -.chart-vertical-zero-line, -.chart-horizontal-zero-line { - -fx-stroke: COLOR_BORDER; -} -.chart-series-line { - -fx-stroke-width: 2px; -} -.default-color0.chart-series-line { -fx-stroke: COLOR_CHART_GREEN; } -.default-color1.chart-series-line { -fx-stroke: COLOR_CHART_RED; } - -/******************************************************************************* - * * - * Dialog * - * * - ******************************************************************************/ - -.dialog-pane { - -fx-background-color: COLOR_BACKGROUND; - -fx-padding: 20px 20px 20px 96px; - - -fx-background-image: url("/img/dialog-appicon.png"); - -fx-background-repeat: no-repeat; - -fx-background-position: 20px 20px; -} - -/* HEADER */ -.dialog-pane:header .header-panel { - -fx-padding: 0 0 12px 0; -} - -/* TITLE */ -.dialog-pane:header .header-panel .label { - -fx-font-weight: bold; - -fx-wrap-text: true; -} - -/* CONTENT LABEL */ -.dialog-pane > .content { - -fx-alignment: top-left; - -fx-wrap-text: true; - -fx-font-size: 11px; - -fx-line-spacing: 1.0; -} - -/* BUTTONS */ -.dialog-pane > .button-bar > .container { - -fx-padding: 12px 0 0 0; -} - -.dialog-pane > .button-bar .button:default { - -fx-background-color: COLOR_HGRAD_BTN_DEF_BORDER, COLOR_HGRAD_BTN_DEF_BACKGROUND; - -fx-text-fill: #FFF; -} - -.dialog-pane > .button-bar .button:default:armed { - -fx-background-color: COLOR_HGRAD_BTN_ARMED_BORDER, COLOR_HGRAD_BTN_ARMED_BACKGROUND; - -fx-text-fill: #FFF; -} \ No newline at end of file diff --git a/main/ui/src/main/resources/css/win_theme.css b/main/ui/src/main/resources/css/win_theme.css deleted file mode 100644 index cdf67b4b0..000000000 --- a/main/ui/src/main/resources/css/win_theme.css +++ /dev/null @@ -1,632 +0,0 @@ -/* - * Copyright (c) 2015 Sebastian Stenzel - * This file is licensed under the terms of the MIT license. - * See the LICENSE.txt file for more info. - * - * Contributors: - * Sebastian Stenzel - initial API and implementation - * Jean-Noël Charon - implementation of the dialog css - * - */ - -.root { - -fx-font-family: 'Segoe UI Semibold'; - -fx-font-smoothing-type: lcd; - -fx-font-size: 12px; - - COLOR_TEXT: #000; - COLOR_TEXT_DISABLED: #888; - COLOR_HYPERLINK: #3399FF; - COLOR_BORDER: #ACACAC; - COLOR_BORDER_FOCUS: #3399FF; - COLOR_CONTROL_BASE: #EAEAEA; - COLOR_BACKGROUND: #F0F0F0; - - COLOR_CHART_GREEN: #A1CD5f; - COLOR_CHART_RED: #C75050; - - COLOR_HGRAD_BTN_BACKGROUND: linear-gradient(to bottom, #F0F0F0 0%, #E5E5E5 100%); - COLOR_HGRAD_BTN_DISABLED_BORDER: linear-gradient(to bottom, #D2D2D2 0%, #C4C4C4 100%); - COLOR_HGRAD_BTN_ARMED_BACKGROUND: linear-gradient(to bottom, #DAECFC 0%, #C4E0FC 100%); - - -fx-background-color: COLOR_BACKGROUND; - -fx-text-fill: COLOR_TEXT; -} - -/**************************************************************************** - * * - * Labels & Fonts * - * * - ****************************************************************************/ - -.label { - -fx-text-fill: COLOR_TEXT; -} - -.ionicons { - -fx-font-family: Ionicons; -} - -.fontawesome { - -fx-font-family: FontAwesome; -} - -.caption-label { - -fx-font-size: 0.9em; -} - -/**************************************************************************** - * * - * Hyperlinks * - * * - ****************************************************************************/ - -.hyperlink { - -fx-cursor: hand; - -fx-text-fill: COLOR_HYPERLINK; -} - -.hyperlink:hover { - -fx-underline: true; -} - -/**************************************************************************** - * * - * Buttons * - * * - ****************************************************************************/ - -.button, -.toggle-button { - -fx-pref-height: 27px; - -fx-background-color: COLOR_BORDER, COLOR_HGRAD_BTN_BACKGROUND; - -fx-background-insets: 0, 1; - -fx-padding: 2px 12px 2px 12px; - -fx-text-fill: COLOR_TEXT; - -fx-alignment: CENTER; - -fx-border-color: transparent; - -fx-border-insets: 2px; -} - -.button:default { - -fx-background-color: COLOR_BORDER_FOCUS, COLOR_HGRAD_BTN_BACKGROUND; -} - -.button:disabled, -.button:default:disabled { - -fx-background-color: COLOR_HGRAD_BTN_DISABLED_BORDER, #F2F2F2; - -fx-text-fill: #8B8B8B; -} - -.button:armed, -.button:default:armed, -.toggle-button:armed, -.toggle-button:selected { - -fx-background-color: COLOR_BORDER_FOCUS, COLOR_HGRAD_BTN_ARMED_BACKGROUND; -} - -.button:focused, -.toggle-button:focused { - -fx-border-color: black; - -fx-border-insets: 2px; - -fx-border-style: dotted inside; -} - -/**************************************************************************** - * * - * Segmented Buttons * - * * - ****************************************************************************/ - -.segmented-button-bar .button, -.segmented-button-bar .toggle-button { - -fx-background-insets: 0, 1; -} - -.segmented-button-bar .button.last, -.segmented-button-bar .toggle-button.last { - -fx-background-insets: 0, 1 1 1 0; -} - -/**************************************************************************** - * * - * Text Fields * - * * - ****************************************************************************/ - -.text-input { - -fx-background-color: COLOR_BORDER, #FFF; - -fx-background-insets: 0, 1; - -fx-text-fill: COLOR_TEXT; - -fx-padding: 0.4em; - -fx-cursor: text; -} -.text-input:focused { - -fx-background-color: COLOR_BORDER_FOCUS, #FFF; -} -.text-input:disabled { - -fx-background-color: COLOR_BORDER, #F1F1F1; - -fx-text-fill: COLOR_TEXT_DISABLED; -} - -/**************************************************************************** - * * - * Vault List * - * * - ****************************************************************************/ - -.list-view { - -fx-background-color: COLOR_BORDER, #FFF; - -fx-border-style: dotted inside; - -fx-border-color: transparent; - -fx-background-insets: 0, 1; - -fx-padding: 1; -} - -.list-view:focused { - -fx-border-color: black; -} - -.list-view > .virtual-flow > .scroll-bar:vertical{ - -fx-background-insets: 0, 0 0 0 1; - -fx-padding: -1 -1 -1 0; -} - -.list-view > .virtual-flow > .scroll-bar:horizontal{ - -fx-background-insets: 0, 1 0 0 0; - -fx-padding: 0 -1 -1 -1; -} - -.list-view > .virtual-flow > .corner { - -fx-background-color: COLOR_BORDER, COLOR_CONTROL_BASE; - -fx-background-insets: 0, 1 0 0 1; -} - -.list-cell { - -fx-background-color: #FFF; - -fx-padding: 0.6em; - -fx-text-fill: COLOR_TEXT; - -fx-opacity: 1; -} - -.list-view:focused > .virtual-flow > .clipped-container > .sheet > .list-cell:focused { - -fx-background-color: COLOR_BORDER_FOCUS, #FFF; - -fx-background-insets: 0, 1; -} - -.list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected { - -fx-background-color: #DEDEDE, #F7F7F7; - -fx-background-insets: 0, 1; -} - -.list-cell:filled:hover { - -fx-background-color: #F7F7F7; -} - -.list-cell .detail-label { - -fx-text-fill: COLOR_TEXT_DISABLED; - -fx-font-size: 0.8em; -} - -/******************************************************************************* - * * - * ScrollBar * - * * - ******************************************************************************/ -.scroll-bar:horizontal, -.scroll-bar:vertical { - -fx-background-color: COLOR_CONTROL_BASE; -} - -.scroll-bar:disabled { - -fx-opacity: 0.6; -} -.scroll-bar > .thumb { - -fx-background-color: #CDCDCD; -} -.scroll-bar > .thumb:hover { - -fx-background-color: #A6A6A6; -} - -.scroll-bar > .increment-button, -.scroll-bar > .decrement-button { - -fx-background-color: transparent; - -fx-color: transparent; -} - -.scroll-bar:horizontal > .increment-button, -.scroll-bar:horizontal > .decrement-button, -.scroll-bar:vertical > .increment-button, -.scroll-bar:vertical > .decrement-button { - -fx-padding: 5px 5px; -} - -.scroll-bar > .increment-button, -.scroll-bar > .decrement-button { - -fx-background-color: derive(COLOR_CONTROL_BASE,-23%), linear-gradient(to bottom, derive(COLOR_CONTROL_BASE,75%), derive(COLOR_CONTROL_BASE,2%)), linear-gradient(to bottom, derive(COLOR_CONTROL_BASE,10%) ,derive(COLOR_CONTROL_BASE,-6%)); - -fx-background-insets: 0, 1, 2; - -fx-color: transparent; - -fx-padding: 3px; -} -.scroll-bar > .increment-button > .increment-arrow, -.scroll-bar > .decrement-button > .decrement-arrow, -.scroll-bar > .increment-button:hover > .increment-arrow, -.scroll-bar > .decrement-button:hover > .decrement-arrow, -.scroll-bar > .increment-button:pressed > .increment-arrow, -.scroll-bar > .decrement-button:pressed > .decrement-arrow { - -fx-background-color: #606060; -} -.scroll-bar:horizontal > .increment-button > .increment-arrow { - -fx-padding: 9 7 0 0; - -fx-shape: "M0.315,1.457l1.414-1.414L5.686,4L1.729,7.957L0.315,6.543L2.857,4L0.315,1.457z"; -} -.scroll-bar:vertical > .increment-button > .increment-arrow { - -fx-padding: 7 9 0 0 ; - -fx-shape: "M6.543,0.315l1.414,1.414L4,5.686L0.043,1.729l1.414-1.414L4,2.858L6.543,0.315z"; -} -.scroll-bar:horizontal > .decrement-button > .decrement-arrow { - -fx-padding: 9 7 0 0; - -fx-shape: "M5.686,6.543L4.271,7.957L0.314,4l3.957-3.957l1.414,1.414L3.143,4L5.686,6.543z"; -} -.scroll-bar:vertical > .decrement-button > .decrement-arrow { - -fx-padding: 7 9 0 0; - -fx-shape: "M1.457,5.563L0.042,4.149L4,0.193l3.957,3.957L6.543,5.563L4,3.021L1.457,5.563z"; -} - -/**************************************************************************** - * * - * Vault List Toolbar * - * * - ****************************************************************************/ - -.tool-bar.list-related-toolbar { - -fx-background-color: transparent; - -fx-padding: 0.4em 0 0 0; - -fx-spacing: 1px; - -fx-alignment: CENTER_LEFT; -} - -.tool-bar.list-related-toolbar .toggle-button, -.tool-bar.list-related-toolbar .button { - -fx-font-size: 1.4em; - -fx-text-fill: COLOR_TEXT; -} - -.tool-bar.list-related-toolbar .toggle-button:disabled, -.tool-bar.list-related-toolbar .button:disabled { - -fx-text-fill: COLOR_TEXT_DISABLED; -} - -/******************************************************************************* - * * - * PopupMenu * - * * - ******************************************************************************/ - -.context-menu { - -fx-font-size: 12px; - -fx-background-color: derive(COLOR_BACKGROUND, -30%), COLOR_BACKGROUND; - -fx-background-insets: 0, 1; - -fx-padding: 1px; - -fx-effect: dropshadow(three-pass-box, rgba(0.0,0.0,0.0,0.2), 2.0, 0.0, 3.0, 3.0); -} -.context-menu > .separator { - -fx-padding: 0.0em 0.333333em 0.0em 0.333333em; /* 0 4 0 4 */ -} -.menu-item { - -fx-background-color: transparent; - -fx-background-insets:0.0; - -fx-padding:0.2em 1em 0.2em 1em; - -fx-border-width: 0.0 0.0 0.0 0.0; - -fx-border-color:transparent; -} -.menu-item > .graphic-container { - -fx-padding: 0 6px 0 0; -} -.menu-item >.label { - -fx-padding: 0em 0.5em 0em 0em; - -fx-text-fill: COLOR_TEXT; -} -.menu-item:disabled > .label { - -fx-opacity: 0.6; -} -.menu-item:focused { - -fx-background-color: COLOR_BORDER_FOCUS, linear-gradient(to bottom, #DAECFC 0%, #C4E0FC 100%); - -fx-background-insets: 0, 1; -} -.menu-item:disabled { - -fx-opacity: 0.6; -} - -/******************************************************************************* - * * - * Tooltip * - * * - ******************************************************************************/ - -.tooltip { - -fx-background-color: COLOR_BACKGROUND; - -fx-padding: 0.2em 0.4em 0.2em 0.4em; - -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.8), 2, 0, 0, 0); - -fx-font-size: 0.9em; -} - -/**************************************************************************** - * * - * Separator * - * * - ****************************************************************************/ - -.separator .line { - -fx-border-style: solid; - -fx-border-width: 1px; - -fx-background-color: null; -} - -.separator:horizontal .line { - -fx-border-color: COLOR_BORDER transparent transparent transparent; -} - -/******************************************************************************* - * * - * CheckBox * - * * - ******************************************************************************/ - -.check-box { - -fx-label-padding: 0 0 0 6px; - -fx-text-fill: COLOR_TEXT; - -fx-border-style: dotted inside; - -fx-border-color: transparent; -} -.check-box > .box { - -fx-padding: 1px; - -fx-border-color: COLOR_BORDER; - -fx-background-color: #FFF; -} -.check-box > .box > .mark { - -fx-background-color: transparent; - -fx-padding: 4px; - -fx-shape: "M-1,4, L-1,5.5 L3.5,8.5 L9,0 L9,-1 L7,-1 L3,6 L1,4 Z"; -} -/* armed/hover: */ -.check-box:hover > .box, -.check-box:armed > .box { - -fx-border-color: COLOR_BORDER_FOCUS; -} -/* focused */ -.check-box:focused { - -fx-border-color: black; -} -/* selected: */ -.check-box:selected > .box > .mark { - -fx-background-color: black; -} -/* disabled: */ -.check-box:disabled > .box { - -fx-background-color: #F1F1F1; -} -.check-box:disabled:selected > .box > .mark { - -fx-background-color: COLOR_TEXT_DISABLED; -} - -/******************************************************************************* - * * - * ChoiceBox * - * * - ******************************************************************************/ - -.choice-box { - -fx-background-color: COLOR_BORDER, linear-gradient(to bottom, #F0F0F0 0%, #E5E5E5 100%); - -fx-background-insets: 0, 1; - -fx-background-radius: 0, 0; - -fx-padding: 0.1em 0.6em 0.1em 0.6em; - -fx-text-fill: COLOR_TEXT; -} - -.choice-box:focused { - -fx-border-style: dotted inside; -} - -.choice-box > .open-button > .arrow { - -fx-background-color: transparent, COLOR_TEXT; - -fx-background-insets: 0 0 -1 0, 0; - -fx-padding: 0.166667em 0.333333em 0.166667em 0.333333em; /* 2 4 2 4 */ - -fx-shape: "M 0 0 h 7 l -3.5 4 z"; -} - -.choice-box .context-menu { - -fx-background-color: COLOR_BORDER, #FFF; - -fx-background-insets: 0, 1; -} - -/**************************************************************************** - * * - * ProgressIndicator * - * * - ****************************************************************************/ - -.progress-indicator { - -fx-indeterminate-segment-count: 12; - -fx-spin-enabled: true; -} -.progress-indicator:indeterminate > .spinner { - -fx-padding: 0.083333em; -} -.progress-indicator:indeterminate .segment { - -fx-background-color: COLOR_BORDER_FOCUS; -} -.progress-indicator:indeterminate .segment0 { - -fx-shape:"M10,0C9.998,0,9.995,0,9.992,0C9.991,0,9.991,0,9.99,0C9.988,0,9.986,0,9.985,0S9.982,0,9.981,0S9.979,0,9.978,0S9.975,0,9.974,0S9.972,0,9.971,0C9.969,0,9.968,0,9.966,0H9.965c-0.007,0-0.014,0-0.02,0C9.944,0,9.944,0,9.944,0C9.941,0,9.939,0,9.937,0c-0.77,0.007-1.389,0.634-1.384,1.404C8.557,2.176,9.185,2.8,9.956,2.8c0.001,0,0.003,0,0.004,0H10c0.773-0.002,1.4-0.63,1.4-1.404c0-0.77-0.622-1.393-1.392-1.396C10.006,0,10.003,0,10,0L10,0z"; -} -.progress-indicator:indeterminate .segment1 { - -fx-shape:"M5.688,1.156c-0.236,0-0.476,0.06-0.696,0.187C4.98,1.349,4.969,1.356,4.958,1.363c0,0-0.001,0-0.001,0C4.955,1.364,4.954,1.365,4.952,1.366c-0.001,0-0.002,0.001-0.004,0.002c0,0,0,0-0.001,0C4.944,1.371,4.94,1.373,4.936,1.375c0,0,0,0-0.001,0C4.933,1.377,4.931,1.378,4.929,1.38C4.267,1.772,4.046,2.624,4.438,3.288c0.261,0.444,0.73,0.692,1.212,0.692c0.24,0,0.484-0.062,0.706-0.192l0.034-0.02C7.058,3.378,7.283,2.52,6.896,1.851C6.636,1.405,6.168,1.156,5.688,1.156L5.688,1.156z"; -} -.progress-indicator:indeterminate .segment2 { - -fx-shape:"M2.534,4.326c-0.482,0-0.951,0.25-1.209,0.697C1.323,5.027,1.321,5.029,1.32,5.031l0,0C1.319,5.033,1.318,5.034,1.317,5.036S1.315,5.039,1.314,5.04c0,0.001,0,0.002-0.001,0.002C1.312,5.044,1.311,5.046,1.31,5.048c0,0,0,0,0,0.001C1.309,5.051,1.308,5.053,1.307,5.055C1.302,5.063,1.297,5.071,1.292,5.079l0,0C1.291,5.082,1.29,5.084,1.288,5.087c-0.376,0.67-0.141,1.519,0.529,1.898c0.218,0.123,0.456,0.182,0.69,0.182c0.488,0,0.963-0.255,1.222-0.708l0.02-0.035c0.383-0.671,0.149-1.527-0.521-1.912C3.008,4.386,2.769,4.326,2.534,4.326L2.534,4.326z"; -} -.progress-indicator:indeterminate .segment3 { - -fx-shape:"M1.396,8.648c-0.002,0-0.005,0-0.008,0C0.619,8.652-0.001,9.278,0,10.047c0,0.002,0,0.006,0,0.008l0,0c0,0.019,0,0.037,0,0.056c0,0.001,0,0.002,0,0.003s0,0.003,0,0.004c0.01,0.765,0.632,1.378,1.396,1.378c0.005,0,0.01,0,0.015,0c0.773-0.009,1.395-0.642,1.389-1.415v-0.04C2.794,9.27,2.166,8.648,1.396,8.648L1.396,8.648z"; -} -.progress-indicator:indeterminate .segment4 { - -fx-shape:"M2.579,12.955c-0.242,0-0.487,0.062-0.71,0.194c-0.664,0.391-0.885,1.242-0.499,1.906c0.013,0.021,0.025,0.043,0.038,0.063c0.262,0.436,0.724,0.678,1.197,0.678c0.243,0,0.49-0.063,0.714-0.197c0.665-0.396,0.883-1.257,0.489-1.922l-0.02-0.034C3.526,13.201,3.059,12.955,2.579,12.955L2.579,12.955z"; -} -.progress-indicator:indeterminate .segment5 { - -fx-shape:"M5.772,16.09c-0.489,0-0.965,0.257-1.223,0.712c-0.38,0.671-0.146,1.52,0.523,1.901c0.002,0.001,0.004,0.002,0.007,0.004h0c0.004,0.002,0.008,0.004,0.012,0.007c0,0,0,0,0.001,0c0.001,0.001,0.002,0.002,0.004,0.002c0.001,0.001,0.003,0.002,0.004,0.003c0,0,0.001,0,0.001,0.001c0.012,0.006,0.023,0.013,0.035,0.019c0.214,0.119,0.446,0.176,0.675,0.176c0.489,0,0.963-0.258,1.22-0.716c0.377-0.675,0.137-1.529-0.537-1.908l-0.035-0.02C6.242,16.149,6.006,16.09,5.772,16.09L5.772,16.09z"; -} -.progress-indicator:indeterminate .segment6 { - -fx-shape:"M10.14,17.198c-0.006,0-0.013,0-0.02,0h-0.039c-0.773,0.011-1.394,0.646-1.385,1.419c0.008,0.767,0.631,1.382,1.396,1.382c0.003,0,0.006,0,0.009-0.001c0.024,0,0.051,0,0.075-0.001c0.769-0.016,1.38-0.648,1.367-1.418C11.53,17.813,10.904,17.198,10.14,17.198L10.14,17.198z"; -} -.progress-indicator:indeterminate .segment7 { - -fx-shape:"M14.433,15.97c-0.245,0-0.494,0.064-0.72,0.2l-0.034,0.021c-0.663,0.397-0.88,1.258-0.483,1.922c0.261,0.439,0.725,0.683,1.2,0.683c0.24,0,0.484-0.062,0.707-0.194c0.022-0.013,0.044-0.025,0.065-0.039c0.656-0.399,0.866-1.254,0.469-1.913C15.373,16.212,14.909,15.97,14.433,15.97L14.433,15.97z"; -} -.progress-indicator:indeterminate .segment8 { - -fx-shape:"M17.539,12.748c-0.493,0-0.973,0.261-1.229,0.723l-0.02,0.034c-0.376,0.676-0.133,1.53,0.542,1.907c0.216,0.121,0.45,0.178,0.681,0.178c0.487,0,0.96-0.256,1.217-0.71c0.003-0.006,0.007-0.012,0.01-0.019c0.007-0.013,0.015-0.025,0.021-0.038l0,0c0.002-0.003,0.003-0.005,0.004-0.008c0.369-0.675,0.124-1.521-0.55-1.893C18.001,12.805,17.769,12.748,17.539,12.748L17.539,12.748z"; -} -.progress-indicator:indeterminate .segment9 { - -fx-shape:"M18.603,8.408c-0.011,0-0.021,0-0.031,0c-0.773,0.018-1.388,0.657-1.373,1.431l0.001,0.04c0.015,0.765,0.641,1.377,1.403,1.377c0.008,0,0.016,0,0.023,0c0.77-0.013,1.383-0.646,1.373-1.414c0-0.003,0-0.006,0-0.009l0,0c-0.001-0.019-0.001-0.037-0.001-0.055c0-0.001,0-0.001-0.001-0.002c0-0.002,0-0.004,0-0.006C19.979,9.012,19.358,8.408,18.603,8.408L18.603,8.408z"; -} -.progress-indicator:indeterminate .segment10 { - -fx-shape:"M17.345,4.121c-0.248,0-0.5,0.066-0.728,0.205c-0.659,0.403-0.869,1.266-0.468,1.927l0.021,0.034c0.265,0.435,0.728,0.675,1.202,0.675c0.247,0,0.497-0.065,0.724-0.202c0.659-0.397,0.871-1.252,0.477-1.912c-0.007-0.011-0.013-0.021-0.02-0.032c-0.001-0.002-0.002-0.003-0.002-0.004c-0.001,0-0.001-0.001-0.001-0.002c-0.004-0.005-0.008-0.011-0.011-0.017c0-0.001,0-0.001-0.001-0.001c-0.001-0.002-0.002-0.004-0.004-0.007C18.271,4.358,17.813,4.121,17.345,4.121L17.345,4.121z"; -} -.progress-indicator:indeterminate .segment11 { - -fx-shape:"M14.104,1.039c-0.494,0-0.974,0.264-1.227,0.729c-0.37,0.679-0.12,1.531,0.559,1.903l0.034,0.019c0.214,0.117,0.445,0.173,0.673,0.173c0.495,0,0.976-0.262,1.231-0.726c0.371-0.674,0.129-1.519-0.542-1.894c-0.012-0.006-0.024-0.013-0.036-0.02c-0.007-0.004-0.014-0.008-0.021-0.012c-0.003-0.001-0.006-0.003-0.009-0.005C14.556,1.094,14.329,1.039,14.104,1.039L14.104,1.039z"; -} - -/***************************************************************************** - * * - * I/O Chart * - * * - *****************************************************************************/ - - .chart { - -fx-padding: 5px; -} - -/* legend */ - -.chart-legend { - -fx-text-fill: COLOR_TEXT; - -fx-background-color: transparent; - -fx-padding: 0.4em; -} -.chart-line-symbol { - -fx-background-radius: 5px; - -fx-padding: 5px; -} -.default-color0.chart-line-symbol { -fx-background-color: COLOR_CHART_GREEN; } -.default-color1.chart-line-symbol { -fx-background-color: COLOR_CHART_RED; } - -/* axis */ - -.axis { - -fx-tick-label-font-size: 0.8em; - -fx-tick-label-fill: COLOR_TEXT; -} -.axis-label { - -fx-text-fill: COLOR_TEXT; - -fx-padding: 0 0 0.8em 0; -} -.axis:left { - -fx-border-color: transparent COLOR_BORDER transparent transparent; -} -.axis-tick-mark, -.axis-minor-tick-mark { - -fx-fill: null; - -fx-stroke: #CCC; -} - -/* content */ - -.chart-content { - -fx-padding: 10px; -} -.chart-horizontal-grid-lines { - -fx-stroke: COLOR_BORDER; -} -.chart-alternative-column-fill { - -fx-fill: null; - -fx-stroke: null; -} -.chart-alternative-row-fill { - -fx-fill: null; - -fx-stroke: null; -} -.chart-vertical-zero-line, -.chart-horizontal-zero-line { - -fx-stroke: COLOR_BORDER; -} -.chart-series-line { - -fx-stroke-width: 2px; -} -.default-color0.chart-series-line { -fx-stroke: COLOR_CHART_GREEN; } -.default-color1.chart-series-line { -fx-stroke: COLOR_CHART_RED; } - -/******************************************************************************* - * * - * Dialog * - * * - ******************************************************************************/ - -.dialog-pane { - -fx-background-color: COLOR_BACKGROUND; - -fx-padding: 20px 20px 20px 96px; - - -fx-background-image: url("/img/dialog-appicon.png"); - -fx-background-repeat: no-repeat; - -fx-background-position: 20px 20px; -} - -/* HEADER */ -.dialog-pane:header .header-panel { - -fx-padding: 0 0 12px 0; -} - -/* TITLE */ -.dialog-pane:header .header-panel .label { - -fx-font-weight: bold; - -fx-wrap-text: true; -} - -/* CONTENT LABEL */ -.dialog-pane > .content { - -fx-alignment: top-left; - -fx-wrap-text: true; - -fx-font-size: 11px; - -fx-line-spacing: 1.0; -} - -/* BUTTONS */ -.dialog-pane > .button-bar > .container { - -fx-padding: 20px 0 0 0; -} - -.dialog-pane > .button-bar .button:default { - -fx-background-color: COLOR_BORDER_FOCUS, COLOR_HGRAD_BTN_BACKGROUND; -} - -.dialog-pane > .button-bar .button:default:armed { - -fx-background-color: COLOR_BORDER_FOCUS, COLOR_HGRAD_BTN_ARMED_BACKGROUND; -} - -.alert.confirmation.dialog-pane, -.text-input-dialog.dialog-pane, -.choice-dialog.dialog-pane { - -fx-padding: 20px 20px 20px 80px; - -fx-background-image: url("/img/dialog-confirm.png"); -} - -.alert.information.dialog-pane { - -fx-padding: 20px 20px 20px 80px; - -fx-background-image: url("/img/dialog-information.png"); -} - -.alert.error.dialog-pane { - -fx-padding: 20px 20px 20px 80px; - -fx-background-image: url("/img/dialog-error.png"); -} - -.alert.warning.dialog-pane { - -fx-padding: 20px 20px 20px 80px; - -fx-background-image: url("/img/dialog-warning.png"); -} \ No newline at end of file diff --git a/main/ui/src/main/resources/fxml/change_password.fxml b/main/ui/src/main/resources/fxml/change_password.fxml deleted file mode 100644 index 4e8947346..000000000 --- a/main/ui/src/main/resources/fxml/change_password.fxml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -