فهرست منبع

complete feaure for poc

Armin Schrenk 4 ماه پیش
والد
کامیت
b9e791905b

+ 7 - 0
src/main/java/org/cryptomator/ui/decryptname/CipherAndCleartext.java

@@ -0,0 +1,7 @@
+package org.cryptomator.ui.decryptname;
+
+import java.nio.file.Path;
+
+record CipherAndCleartext(Path ciphertext, String cleartextName) {
+
+}

+ 94 - 0
src/main/java/org/cryptomator/ui/decryptname/CipherAndCleartextCellController.java

@@ -0,0 +1,94 @@
+package org.cryptomator.ui.decryptname;
+
+import org.cryptomator.common.ObservableUtil;
+import org.cryptomator.ui.common.FxController;
+import org.cryptomator.ui.controls.FontAwesome5Icon;
+import org.jetbrains.annotations.NotNull;
+
+import javax.inject.Inject;
+import javafx.beans.binding.Bindings;
+import javafx.beans.binding.StringBinding;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.value.ObservableStringValue;
+import javafx.beans.value.ObservableValue;
+import javafx.fxml.FXML;
+import javafx.scene.control.Tooltip;
+import javafx.scene.layout.HBox;
+import java.util.ResourceBundle;
+
+public class CipherAndCleartextCellController implements FxController {
+
+	private final ObjectProperty<CipherAndCleartext> item = new SimpleObjectProperty<>();
+	private final ObservableValue<String> ciphertext;
+	private final ObservableValue<String> cleartext;
+	private final ObjectProperty<FontAwesome5Icon> icon = new SimpleObjectProperty<>();
+	private final ResourceBundle resourceBundle;
+
+	@FXML
+	public HBox root;
+
+	@Inject
+	public CipherAndCleartextCellController(ResourceBundle resourceBundle) {
+		this.resourceBundle = resourceBundle;
+		this.ciphertext = ObservableUtil.mapWithDefault(item, i -> i.ciphertext().getFileName().toString(), "");
+		this.cleartext = ObservableUtil.mapWithDefault(item, CipherAndCleartext::cleartextName, "");
+	}
+
+	@FXML
+	public void initialize() {
+		icon.bind(Bindings.createObjectBinding(this::selectIcon, root.hoverProperty()));
+	}
+
+	private String selectText() {
+		var cipherAndClear = item.get();
+		if (cipherAndClear != null) {
+			if (root.isHover()) {
+				return cipherAndClear.cleartextName();
+			} else {
+				return cipherAndClear.ciphertext().getFileName().toString();
+			}
+		}
+		return "";
+	}
+
+	private FontAwesome5Icon selectIcon() {
+		if (root.isHover()) {
+			return FontAwesome5Icon.LOCK_OPEN;
+		} else {
+			return FontAwesome5Icon.LOCK;
+		}
+	}
+
+	public void setCipherAndCleartextEntry(@NotNull CipherAndCleartext item) {
+		this.item.set(item);
+		var tooltip = new Tooltip("Click to copy");
+		Tooltip.install(root,tooltip);
+	}
+
+	//observability getter
+	public ObservableValue<String> ciphertextProperty() {
+		return ciphertext;
+	}
+
+	public String getCiphertext() {
+		return ciphertext.getValue();
+	}
+
+	public ObservableValue<String> cleartextProperty() {
+		return cleartext;
+	}
+
+	public String getCleartext() {
+		return cleartext.getValue();
+	}
+
+	public ObservableValue<FontAwesome5Icon> iconProperty() {
+		return icon;
+	}
+
+	public FontAwesome5Icon getIcon() {
+		return icon.getValue();
+	}
+
+}

+ 64 - 0
src/main/java/org/cryptomator/ui/decryptname/CipherAndCleartextCellFactory.java

@@ -0,0 +1,64 @@
+package org.cryptomator.ui.decryptname;
+
+import jakarta.inject.Inject;
+import org.cryptomator.ui.common.FxmlLoaderFactory;
+
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
+import javafx.scene.control.ContentDisplay;
+import javafx.scene.control.ListCell;
+import javafx.scene.control.ListView;
+import javafx.util.Callback;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+
+@DecryptNameScoped
+public class CipherAndCleartextCellFactory implements Callback<ListView<CipherAndCleartext>, ListCell<CipherAndCleartext>> {
+
+	private static final String FXML_PATH = "/fxml/decryptnames_cipherandcleartextcell.fxml";
+	private final FxmlLoaderFactory fxmlLoaders;
+
+	@Inject
+	public CipherAndCleartextCellFactory(@DecryptNameWindow FxmlLoaderFactory fxmlLoaders) {
+		this.fxmlLoaders = fxmlLoaders;
+	}
+
+
+	@Override
+	public ListCell<CipherAndCleartext> call(ListView<CipherAndCleartext> cipherAndCleartextListView) {
+		try {
+			FXMLLoader fxmlLoader = fxmlLoaders.load(FXML_PATH);
+			return new Cell(fxmlLoader.getRoot(), fxmlLoader.getController());
+		} catch (IOException e) {
+			throw new UncheckedIOException("Failed to load %s.".formatted(FXML_PATH), e);
+		}
+	}
+
+	private static class Cell extends ListCell<CipherAndCleartext> {
+
+		private final Parent root;
+		private final CipherAndCleartextCellController controller;
+
+		public Cell(Parent root, CipherAndCleartextCellController controller) {
+			this.root = root;
+			this.controller = controller;
+		}
+
+		@Override
+		protected void updateItem(CipherAndCleartext item, boolean empty) {
+			super.updateItem(item, empty);
+
+			if (empty || item == null) {
+				setGraphic(null);
+				this.getStyleClass().remove("test-list-cell");
+				setVisible(false);
+			} else {
+				this.getStyleClass().addLast("test-list-cell");
+				setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
+				setGraphic(root);
+				controller.setCipherAndCleartextEntry(item);
+				setVisible(true);
+			}
+		}
+	}
+}

+ 0 - 93
src/main/java/org/cryptomator/ui/decryptname/DecryptFileNamesView.java

@@ -1,93 +0,0 @@
-package org.cryptomator.ui.decryptname;
-
-import org.cryptomator.common.vaults.Vault;
-import org.cryptomator.ui.common.FxController;
-import org.cryptomator.ui.controls.FontAwesome5Icon;
-
-import javax.inject.Inject;
-import javafx.beans.property.ListProperty;
-import javafx.beans.property.ObjectProperty;
-import javafx.beans.property.SimpleListProperty;
-import javafx.beans.property.SimpleObjectProperty;
-import javafx.beans.property.SimpleStringProperty;
-import javafx.beans.property.StringProperty;
-import javafx.beans.value.ObservableValue;
-import javafx.collections.FXCollections;
-import javafx.fxml.FXML;
-import javafx.scene.control.Label;
-import javafx.scene.control.ListView;
-import javafx.scene.text.Text;
-import javafx.stage.FileChooser;
-import javafx.stage.Stage;
-import java.io.File;
-import java.nio.file.Path;
-import java.time.temporal.Temporal;
-import java.util.List;
-import java.util.ResourceBundle;
-
-@DecryptNameScoped
-public class DecryptFileNamesView implements FxController {
-
-	private final ListProperty<Path> pathsToDecrypt;
-	private final StringProperty dropZoneText = new SimpleStringProperty();
-	private final ObjectProperty<FontAwesome5Icon> dropZoneIcon = new SimpleObjectProperty<>();
-	private final Stage window;
-	private final Vault vault;
-	private final ResourceBundle resourceBundle;
-
-	@FXML
-	public ListView<Path> decryptedNamesView;
-
-	@Inject
-	public DecryptFileNamesView(@DecryptNameWindow Stage window, @DecryptNameWindow Vault vault, @DecryptNameWindow List<Path> pathsToDecrypt, ResourceBundle resourceBundle) {
-		this.window = window;
-		this.vault = vault;
-		this.resourceBundle = resourceBundle;
-		this.pathsToDecrypt = new SimpleListProperty<>(FXCollections.observableArrayList(pathsToDecrypt));
-	}
-
-	@FXML
-	public void initialize() {
-		decryptedNamesView.setItems(pathsToDecrypt);
-		//decryptedNamesView.setCellFactory(this::createListCell);
-	}
-
-	@FXML
-	public void selectAndDecrypt() {
-		var fileChooser = new FileChooser();
-		fileChooser.setTitle(resourceBundle.getString("main.vaultDetail.decryptName.filePickerTitle"));
-
-		fileChooser.setInitialDirectory(vault.getPath().toFile());
-		var ciphertextNodes = fileChooser.showOpenMultipleDialog(window);
-		if (ciphertextNodes != null) {
-			pathsToDecrypt.clear();
-			pathsToDecrypt.addAll(ciphertextNodes.stream().map(File::toPath).toList());
-		}
-	}
-	//obvservable getter
-
-	public ObservableValue<String> dropZoneTextProperty() {
-		return dropZoneText;
-	}
-
-	public String getDropZoneText() {
-		return dropZoneText.get();
-	}
-
-	public ObservableValue<FontAwesome5Icon> dropZoneIconProperty() {
-		return dropZoneIcon;
-	}
-
-	public FontAwesome5Icon getDropZoneIcon() {
-		return dropZoneIcon.get();
-	}
-
-	public ObservableValue<Boolean> decryptedPathsListEmptyProperty() {
-		return pathsToDecrypt.emptyProperty();
-	}
-
-	public boolean isDecryptedPathsListEmpty() {
-		return pathsToDecrypt.isEmpty();
-	}
-
-}

+ 158 - 0
src/main/java/org/cryptomator/ui/decryptname/DecryptFileNamesViewController.java

@@ -0,0 +1,158 @@
+package org.cryptomator.ui.decryptname;
+
+import org.cryptomator.common.vaults.Vault;
+import org.cryptomator.cryptofs.common.Constants;
+import org.cryptomator.ui.common.FxController;
+import org.cryptomator.ui.controls.FontAwesome5Icon;
+
+import javax.inject.Inject;
+import javafx.application.Platform;
+import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.ListProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleListProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.beans.value.ObservableValue;
+import javafx.collections.FXCollections;
+import javafx.fxml.FXML;
+import javafx.scene.control.ListView;
+import javafx.stage.FileChooser;
+import javafx.stage.Stage;
+import java.io.File;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+@DecryptNameScoped
+public class DecryptFileNamesViewController implements FxController {
+
+	private final ListProperty<CipherAndCleartext> mapping;
+	private final StringProperty dropZoneText = new SimpleStringProperty();
+	private final ObjectProperty<FontAwesome5Icon> dropZoneIcon = new SimpleObjectProperty<>();
+	private final BooleanProperty wrongFilesSelected = new SimpleBooleanProperty(false);
+	private final Stage window;
+	private final Vault vault;
+	private final CipherAndCleartextCellFactory cellFactory;
+	private final ResourceBundle resourceBundle;
+	private final List<Path> initialList;
+
+	@FXML
+	public ListView<CipherAndCleartext> decryptedNamesView;
+
+	@Inject
+	public DecryptFileNamesViewController(@DecryptNameWindow Stage window, @DecryptNameWindow Vault vault, @DecryptNameWindow List<Path> pathsToDecrypt, CipherAndCleartextCellFactory cellFactory, ResourceBundle resourceBundle) {
+		this.window = window;
+		this.vault = vault;
+		this.cellFactory = cellFactory;
+		this.resourceBundle = resourceBundle;
+		this.mapping = new SimpleListProperty<>(FXCollections.observableArrayList());
+		this.initialList = pathsToDecrypt;
+	}
+
+	@FXML
+	public void initialize() {
+		decryptedNamesView.setItems(mapping);
+		decryptedNamesView.setCellFactory(cellFactory);
+
+		dropZoneText.setValue("Drop files or click to select");
+		dropZoneIcon.setValue(FontAwesome5Icon.FILE_IMPORT);
+
+		wrongFilesSelected.addListener((_, _, areWrongFiles) -> {
+			if (areWrongFiles) {
+				CompletableFuture.delayedExecutor(3, TimeUnit.SECONDS, Platform::runLater).execute(() -> {
+					//dropZoneText.setValue(resourceBundle.getString(".."));
+					dropZoneText.setValue("Drop files or click to select");
+					dropZoneIcon.setValue(FontAwesome5Icon.FILE_IMPORT);
+					wrongFilesSelected.setValue(false);
+				});
+			}
+		});
+		if (!initialList.isEmpty()) {
+			checkAndDecrypt(initialList);
+		}
+	}
+
+	@FXML
+	public void selectFiles() {
+		var fileChooser = new FileChooser();
+		fileChooser.setTitle(resourceBundle.getString("main.vaultDetail.decryptName.filePickerTitle"));
+		fileChooser.setSelectedExtensionFilter(new FileChooser.ExtensionFilter("Cryptomator encrypted files", List.of("*.c9r", "*.c9s")));
+		fileChooser.setInitialDirectory(vault.getPath().toFile());
+		var ciphertextNodes = fileChooser.showOpenMultipleDialog(window);
+		if (ciphertextNodes != null) {
+			checkAndDecrypt(ciphertextNodes.stream().map(File::toPath).toList());
+		}
+	}
+
+	private void checkAndDecrypt(List<Path> pathsToDecrypt) {
+		//Assumption: All files are in the same directory
+		var testPath = pathsToDecrypt.getFirst();
+		if (!testPath.startsWith(vault.getPath())) {
+			setDropZoneError("Selected files do not belong the the vault");
+			return;
+		}
+		if (pathsToDecrypt.size() == 1 && testPath.endsWith(Constants.DIR_ID_BACKUP_FILE_NAME)) {
+			setDropZoneError("%s is a vault internal file with no encrypted filename".formatted(Constants.DIR_ID_BACKUP_FILE_NAME));
+			return;
+		}
+
+		try {
+			var newMapping = pathsToDecrypt.stream().filter(p -> !p.endsWith(Constants.DIR_ID_BACKUP_FILE_NAME)).map(this::getCleartextName).toList();
+			mapping.clear();
+			mapping.addAll(newMapping);
+		} catch (UncheckedIOException e) {
+			setDropZoneError("Failed to read selected files");
+		} catch (IllegalArgumentException e) {
+			setDropZoneError("Names of selected files are not encrypted".formatted(Constants.DIR_ID_BACKUP_FILE_NAME));
+		}
+	}
+
+	private void setDropZoneError(String text) {
+		dropZoneIcon.setValue(FontAwesome5Icon.TIMES);
+		dropZoneText.setValue(text);
+		wrongFilesSelected.setValue(true);
+	}
+
+	private CipherAndCleartext getCleartextName(Path ciphertextNode) {
+		try {
+			var cleartextName = vault.getCleartextName(ciphertextNode);
+			return new CipherAndCleartext(ciphertextNode, cleartextName);
+		} catch (IOException e) {
+			throw new UncheckedIOException(e);
+		}
+	}
+
+	//obvservable getter
+
+	public ObservableValue<String> dropZoneTextProperty() {
+		return dropZoneText;
+	}
+
+	public String getDropZoneText() {
+		return dropZoneText.get();
+	}
+
+	public ObservableValue<FontAwesome5Icon> dropZoneIconProperty() {
+		return dropZoneIcon;
+	}
+
+	public FontAwesome5Icon getDropZoneIcon() {
+		return dropZoneIcon.get();
+	}
+
+	public ObservableValue<Boolean> decryptedPathsListEmptyProperty() {
+		return mapping.emptyProperty();
+	}
+
+	public boolean isDecryptedPathsListEmpty() {
+		return mapping.isEmpty();
+	}
+
+}

+ 8 - 3
src/main/java/org/cryptomator/ui/decryptname/DecryptNameModule.java

@@ -48,12 +48,17 @@ public abstract class DecryptNameModule {
 	@Provides
 	@FxmlScene(FxmlFile.DECRYPTNAMES)
 	@DecryptNameScoped
-	static Scene provideOverviewScene(@DecryptNameWindow FxmlLoaderFactory fxmlLoaders) {
+	static Scene provideDecryptNamesViewScene(@DecryptNameWindow FxmlLoaderFactory fxmlLoaders) {
 		return fxmlLoaders.createScene(FxmlFile.DECRYPTNAMES);
 	}
 
 	@Binds
 	@IntoMap
-	@FxControllerKey(DecryptFileNamesView.class)
-	abstract FxController bindOverviewController(DecryptFileNamesView controller);
+	@FxControllerKey(DecryptFileNamesViewController.class)
+	abstract FxController bindDecryptNamesViewController(DecryptFileNamesViewController controller);
+
+	@Binds
+	@IntoMap
+	@FxControllerKey(CipherAndCleartextCellController.class)
+	abstract FxController binCipherAndCleartextCellController(CipherAndCleartextCellController controller);
 }

+ 8 - 10
src/main/resources/css/dark_theme.css

@@ -1001,10 +1001,13 @@
 	-fx-background-color: CONTROL_BG_NORMAL;
 }
 
-.test-list-view:focused .list-cell:selected {
+.test-list-view:focused .test-list-cell:selected {
 	-fx-background-color: PRIMARY, CONTROL_BG_SELECTED;
 	-fx-background-insets: 0, 0 0 0 3px;
 }
+.test-list-view .test-list-cell:hover {
+	-fx-background-color: CONTROL_BG_SELECTED;
+}
 
 .test-list-cell:selected {
 	-fx-background-color: CONTROL_BG_SELECTED;
@@ -1014,20 +1017,15 @@
 	-fx-fill: TEXT_FILL_MUTED;
 }
 
-.test-list-cell .header-label {
-	-fx-font-family: 'Open Sans SemiBold';
+.test-list-cell .text {
+	-fx-fill: TEXT_FILL;
 	-fx-font-size: 1.0em;
 }
 
-.test-list-cell .detail-label {
-	-fx-text-fill: TEXT_FILL_MUTED;
-	-fx-font-size: 0.8em;
-}
-
 .test-list-cell:selected .glyph-icon {
 	-fx-fill: PRIMARY;
 }
 
-.test-list-cell:selected .header-label {
-	-fx-text-fill: TEXT_FILL_HIGHLIGHTED;
+.test-list-cell:selected .text {
+	-fx-fill: TEXT_FILL_HIGHLIGHTED;
 }

+ 57 - 1
src/main/resources/css/light_theme.css

@@ -971,4 +971,60 @@
 	-fx-background-color: CONTROL_BORDER_NORMAL, CONTROL_BG_NORMAL;
 	-fx-background-insets: 0, 1px;
 	-fx-background-radius: 4px;
-}
+}
+
+.test-style {
+	/*-fx-background-color: rgba(88,94,98,0.7), rgba(53,57,59,0.7) ;*/
+	-fx-background-color: CONTROL_BORDER_NORMAL, MAIN_BG;
+	-fx-background-insets: 0, 1px;
+	-fx-background-radius: 4px;
+}
+
+.test-style2 {
+	/*-fx-background-color: rgba(88,94,98,0.7), rgba(53,57,59,0.7) ;*/
+	-fx-background-color: CONTROL_BG_NORMAL;
+	-fx-background-insets: 1px;
+	-fx-background-radius: 4px;
+	-fx-border-width: 4px;
+	-fx-border-style: dashed inside;
+	-fx-border-color: CONTROL_BORDER_NORMAL;
+	-fx-border-radius: 4px;
+}
+
+.test-style2:hover {
+	/*-fx-background-color: rgba(88,94,98,0.7), rgba(53,57,59,0.7) ;*/
+	-fx-background-color: CONTROL_BG_HOVER;
+}
+
+.test-list-view {
+	-fx-background-color: CONTROL_BG_NORMAL;
+}
+
+.test-list-view:focused .test-list-cell:selected {
+	-fx-background-color: PRIMARY, CONTROL_BG_SELECTED;
+	-fx-background-insets: 0, 0 0 0 3px;
+}
+.test-list-view .test-list-cell:hover {
+	-fx-background-color: CONTROL_BG_SELECTED;
+}
+
+.test-list-cell:selected {
+	-fx-background-color: CONTROL_BG_SELECTED;
+}
+
+.test-list-cell .glyph-icon {
+	-fx-fill: TEXT_FILL_MUTED;
+}
+
+.test-list-cell .text {
+	-fx-fill: TEXT_FILL;
+	-fx-font-size: 1.0em;
+}
+
+.test-list-cell:selected .glyph-icon {
+	-fx-fill: PRIMARY;
+}
+
+.test-list-cell:selected .text {
+	-fx-fill: TEXT_FILL_HIGHLIGHTED;
+}

+ 6 - 9
src/main/resources/fxml/decryptnames.fxml

@@ -1,16 +1,13 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
-<?import java.lang.*?>
-<?import java.util.*?>
-<?import javafx.scene.*?>
-<?import javafx.scene.control.*?>
-<?import javafx.scene.layout.*?>
-
-<?import javafx.geometry.Insets?>
 <?import org.cryptomator.ui.controls.FontAwesome5IconView?>
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.control.ListView?>
+<?import javafx.scene.layout.VBox?>
 <VBox xmlns="http://javafx.com/javafx"
 	  xmlns:fx="http://javafx.com/fxml"
-	  fx:controller="org.cryptomator.ui.decryptname.DecryptFileNamesView"
+	  fx:controller="org.cryptomator.ui.decryptname.DecryptFileNamesViewController"
 	  minWidth="400"
 	  maxWidth="400"
 	  minHeight="145">
@@ -18,7 +15,7 @@
 		<Insets topRightBottomLeft="24"/>
 	</padding>
 	<Label text="Decrypt File Name" styleClass="label-large"/>
-	<VBox alignment="CENTER" VBox.vgrow="ALWAYS" styleClass="test-style2" visible="${controller.decryptedPathsListEmpty}" managed="${controller.decryptedPathsListEmpty}" onMouseClicked="#selectAndDecrypt">
+	<VBox alignment="CENTER" VBox.vgrow="ALWAYS" styleClass="test-style2" visible="${controller.decryptedPathsListEmpty}" managed="${controller.decryptedPathsListEmpty}" onMouseClicked="#selectFiles">
 		<!-- Drop or click to decrypt names of files -->
 		<Label text="${controller.dropZoneText}" contentDisplay="TOP">
 			<graphic>

+ 23 - 0
src/main/resources/fxml/decryptnames_cipherandcleartextcell.fxml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.layout.HBox?>
+<?import org.cryptomator.ui.controls.FontAwesome5IconView?>
+<?import javafx.scene.text.Text?>
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.layout.StackPane?>
+<HBox xmlns="http://javafx.com/javafx"
+	  xmlns:fx="http://javafx.com/fxml"
+	  fx:controller="org.cryptomator.ui.decryptname.CipherAndCleartextCellController"
+	  spacing="6"
+	  fx:id="root"
+		>
+	<padding>
+		<Insets topRightBottomLeft="6"/>
+	</padding>
+	<FontAwesome5IconView glyph="${controller.icon}" glyphSize="16" />
+	<StackPane alignment="CENTER_LEFT">
+		<Label styleClass="label" textOverrun="CENTER_ELLIPSIS" text="${controller.ciphertext}" visible="${!root.hover}"/>
+		<Label styleClass="label" textOverrun="CENTER_ELLIPSIS" text="${controller.cleartext}" visible="${root.hover}"/>
+	</StackPane>
+</HBox>

+ 1 - 0
src/main/resources/fxml/vault_detail_unlocked.fxml

@@ -58,6 +58,7 @@
 					<Tooltip text="%main.vaultDetail.locateEncryptedFileBtn.tooltip"/>
 				</tooltip>
 			</Button>
+			<!-- TODO: revert removing te copied to button -->
 		</StackPane>
 		<!-- decrypt file name -->
 		<Button fx:id="decryptNameDropZone" styleClass="drag-n-drop" text="%main.vaultDetail.decryptName.buttonLabel" minWidth="120" maxWidth="180" prefHeight="72" wrapText="true" textAlignment="CENTER" onAction="#showDecryptNameWindow" contentDisplay="TOP">