This commit is contained in:
Sebastian Stenzel
2014-12-15 09:39:11 +01:00
9 changed files with 108 additions and 25 deletions
@@ -16,12 +16,15 @@ import java.util.ResourceBundle;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent; import javafx.event.ActionEvent;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.Parent; import javafx.scene.Parent;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.ListCell; import javafx.scene.control.ListCell;
import javafx.scene.control.ListView; import javafx.scene.control.ListView;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
@@ -44,6 +47,9 @@ public class MainController implements Initializable, InitializationListener, Un
private Stage stage; private Stage stage;
@FXML
private ContextMenu directoryContextMenu;
@FXML @FXML
private HBox rootPane; private HBox rootPane;
@@ -58,9 +64,11 @@ public class MainController implements Initializable, InitializationListener, Un
@Override @Override
public void initialize(URL url, ResourceBundle rb) { public void initialize(URL url, ResourceBundle rb) {
this.rb = rb; this.rb = rb;
final ObservableList<Directory> items = FXCollections.observableList(Settings.load().getDirectories());
directoryList.setItems(items);
directoryList.setCellFactory(this::createDirecoryListCell); directoryList.setCellFactory(this::createDirecoryListCell);
directoryList.getSelectionModel().getSelectedItems().addListener(this::selectedDirectoryDidChange); directoryList.getSelectionModel().getSelectedItems().addListener(this::selectedDirectoryDidChange);
directoryList.getItems().addAll(Settings.load().getDirectories());
} }
@FXML @FXML
@@ -70,22 +78,38 @@ public class MainController implements Initializable, InitializationListener, Un
if (file != null && file.canWrite()) { if (file != null && file.canWrite()) {
final Directory dir = new Directory(file.toPath()); final Directory dir = new Directory(file.toPath());
directoryList.getItems().add(dir); directoryList.getItems().add(dir);
Settings.load().getDirectories().clear();
Settings.load().getDirectories().addAll(directoryList.getItems());
directoryList.getSelectionModel().selectLast(); directoryList.getSelectionModel().selectLast();
} }
} }
private ListCell<Directory> createDirecoryListCell(ListView<Directory> param) { private ListCell<Directory> createDirecoryListCell(ListView<Directory> param) {
return new DirectoryListCell(); final DirectoryListCell cell = new DirectoryListCell();
cell.setContextMenu(directoryContextMenu);
return cell;
} }
private void selectedDirectoryDidChange(ListChangeListener.Change<? extends Directory> change) { private void selectedDirectoryDidChange(ListChangeListener.Change<? extends Directory> change) {
final Directory selectedDir = directoryList.getSelectionModel().getSelectedItem(); final Directory selectedDir = directoryList.getSelectionModel().getSelectedItem();
stage.setTitle(selectedDir.getName()); if (selectedDir == null) {
showDirectory(selectedDir); stage.setTitle(rb.getString("app.name"));
showWelcomeView();
} else {
stage.setTitle(selectedDir.getName());
showDirectory(selectedDir);
}
} }
@FXML
private void didClickRemoveSelectedEntry(ActionEvent e) {
final Directory selectedDir = directoryList.getSelectionModel().getSelectedItem();
directoryList.getItems().remove(selectedDir);
directoryList.getSelectionModel().clearSelection();
}
// ****************************************
// Subcontroller for right panel
// ****************************************
private void showDirectory(Directory directory) { private void showDirectory(Directory directory) {
try { try {
if (directory.isUnlocked()) { if (directory.isUnlocked()) {
@@ -100,10 +124,6 @@ public class MainController implements Initializable, InitializationListener, Un
} }
} }
// ****************************************
// Subcontroller for right panel
// ****************************************
private <T> T showView(String fxml) { private <T> T showView(String fxml) {
try { try {
final FXMLLoader loader = new FXMLLoader(getClass().getResource(fxml), rb); final FXMLLoader loader = new FXMLLoader(getClass().getResource(fxml), rb);
@@ -116,6 +136,10 @@ public class MainController implements Initializable, InitializationListener, Un
} }
} }
private void showWelcomeView() {
this.showView("/welcome.fxml");
}
private void showInitializeView(Directory directory) { private void showInitializeView(Directory directory) {
final InitializeController ctrl = showView("/initialize.fxml"); final InitializeController ctrl = showView("/initialize.fxml");
ctrl.setDirectory(directory); ctrl.setDirectory(directory);
@@ -77,7 +77,7 @@ public class UnlockController implements Initializable {
// **************************************** // ****************************************
@FXML @FXML
protected void didClickUnlockButton(ActionEvent event) { private void didClickUnlockButton(ActionEvent event) {
final String masterKeyFileName = usernameBox.getValue() + Aes256Cryptor.MASTERKEY_FILE_EXT; final String masterKeyFileName = usernameBox.getValue() + Aes256Cryptor.MASTERKEY_FILE_EXT;
final Path masterKeyPath = directory.getPath().resolve(masterKeyFileName); final Path masterKeyPath = directory.getPath().resolve(masterKeyFileName);
final CharSequence password = passwordField.getCharacters(); final CharSequence password = passwordField.getCharacters();
@@ -52,7 +52,7 @@ public class UnlockedController implements Initializable {
} }
@FXML @FXML
protected void closeVault(ActionEvent event) { private void didClickCloseVault(ActionEvent event) {
directory.unmount(); directory.unmount();
directory.stopServer(); directory.stopServer();
directory.setUnlocked(false); directory.setUnlocked(false);
@@ -82,7 +82,7 @@ public class UnlockedController implements Initializable {
private class IoSamplingAnimationHandler implements EventHandler<ActionEvent> { private class IoSamplingAnimationHandler implements EventHandler<ActionEvent> {
private static final double BYTES_TO_MEGABYTES_FACTOR = IO_SAMPLING_INTERVAL / 1024.0 / 1024.0; private static final double BYTES_TO_MEGABYTES_FACTOR = 1.0 / IO_SAMPLING_INTERVAL / 1024.0 / 1024.0;
private final CryptorIOSampling sampler; private final CryptorIOSampling sampler;
private final Series<Number, Number> decryptedBytes; private final Series<Number, Number> decryptedBytes;
private final Series<Number, Number> encryptedBytes; private final Series<Number, Number> encryptedBytes;
@@ -1,19 +1,55 @@
package org.cryptomator.ui.controls; package org.cryptomator.ui.controls;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ListCell; import javafx.scene.control.ListCell;
import javafx.scene.control.Tooltip;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Circle;
import org.cryptomator.ui.model.Directory; import org.cryptomator.ui.model.Directory;
public class DirectoryListCell extends ListCell<Directory> { public class DirectoryListCell extends ListCell<Directory> implements ChangeListener<Boolean> {
// TODO: fancy graphics instead of circles ;-)
private final Circle statusIndicator = new Circle(3.0);
public DirectoryListCell() {
setGraphic(statusIndicator);
setGraphicTextGap(12.0);
setContentDisplay(ContentDisplay.LEFT);
}
@Override @Override
protected void updateItem(Directory item, boolean empty) { protected void updateItem(Directory item, boolean empty) {
final Directory oldItem = super.getItem();
if (oldItem != null) {
oldItem.unlockedProperty().removeListener(this);
}
super.updateItem(item, empty); super.updateItem(item, empty);
if (item == null) { if (item == null) {
setText(null); setText(null);
setTooltip(null);
statusIndicator.setVisible(false);
} else { } else {
setText(item.getName()); setText(item.getName());
setTooltip(new Tooltip(item.getPath().toString()));
statusIndicator.setVisible(true);
item.unlockedProperty().addListener(this);
updateStatusIndicator();
} }
} }
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
updateStatusIndicator();
}
private void updateStatusIndicator() {
final Paint statusColor = getItem().isUnlocked() ? Color.LIME : Color.RED;
statusIndicator.setFill(statusColor);
}
} }
@@ -5,6 +5,9 @@ import java.io.Serializable;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.cryptomator.crypto.Cryptor; import org.cryptomator.crypto.Cryptor;
import org.cryptomator.crypto.SamplingDecorator; import org.cryptomator.crypto.SamplingDecorator;
@@ -29,8 +32,9 @@ public class Directory implements Serializable {
private final WebDAVServer server = new WebDAVServer(); private final WebDAVServer server = new WebDAVServer();
private final Cryptor cryptor = SamplingDecorator.decorate(new Aes256Cryptor()); private final Cryptor cryptor = SamplingDecorator.decorate(new Aes256Cryptor());
private final ObjectProperty<Boolean> unlocked = new SimpleObjectProperty<Boolean>(this, "unlocked", Boolean.FALSE);
private final Path path; private final Path path;
private boolean unlocked; // private boolean unlocked;
private String unmountCommand; private String unmountCommand;
private final Runnable shutdownTask = new ShutdownTask(); private final Runnable shutdownTask = new ShutdownTask();
@@ -103,12 +107,16 @@ public class Directory implements Serializable {
return cryptor; return cryptor;
} }
public boolean isUnlocked() { public ObjectProperty<Boolean> unlockedProperty() {
return unlocked; return unlocked;
} }
public boolean isUnlocked() {
return unlocked.get();
}
public void setUnlocked(boolean unlocked) { public void setUnlocked(boolean unlocked) {
this.unlocked = unlocked; this.unlocked.set(unlocked);
} }
public WebDAVServer getServer() { public WebDAVServer getServer() {
@@ -18,7 +18,7 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.List;
import org.apache.commons.lang3.SystemUtils; import org.apache.commons.lang3.SystemUtils;
import org.cryptomator.ui.model.Directory; import org.cryptomator.ui.model.Directory;
@@ -54,7 +54,7 @@ public class Settings implements Serializable {
} }
} }
private Collection<Directory> directories; private List<Directory> directories;
private String username; private String username;
private Settings() { private Settings() {
@@ -96,14 +96,14 @@ public class Settings implements Serializable {
/* Getter/Setter */ /* Getter/Setter */
public Collection<Directory> getDirectories() { public List<Directory> getDirectories() {
if (directories == null) { if (directories == null) {
directories = new ArrayList<>(); directories = new ArrayList<>();
} }
return directories; return directories;
} }
public void setDirectories(Collection<Directory> directories) { public void setDirectories(List<Directory> directories) {
this.directories = directories; this.directories = directories;
} }
@@ -9,6 +9,11 @@
app.name=Cryptomator app.name=Cryptomator
# main.fxml
main.directoryList.contextMenu.remove=Remove from list
main.directoryList.contextMenu.addUser=Add user
main.directoryList.contextMenu.changePassword=Change password
# welcome.fxml # welcome.fxml
welcome.welcomeLabel=Welcome to Cryptomator welcome.welcomeLabel=Welcome to Cryptomator
+11 -1
View File
@@ -14,11 +14,21 @@
<?import javafx.scene.layout.Pane?> <?import javafx.scene.layout.Pane?>
<?import javafx.scene.control.ToolBar?> <?import javafx.scene.control.ToolBar?>
<?import javafx.scene.control.Button?> <?import javafx.scene.control.Button?>
<?import javafx.scene.control.ContextMenu?>
<?import javafx.scene.control.MenuItem?>
<HBox fx:id="rootPane" prefHeight="400.0" prefWidth="600.0" fx:controller="org.cryptomator.ui.MainController" xmlns:fx="http://javafx.com/fxml"> <HBox fx:id="rootPane" prefHeight="400.0" prefWidth="600.0" fx:controller="org.cryptomator.ui.MainController" xmlns:fx="http://javafx.com/fxml">
<fx:define> <fx:define>
<fx:include fx:id="welcomeView" source="welcome.fxml" /> <fx:include fx:id="welcomeView" source="welcome.fxml" />
<ContextMenu fx:id="directoryContextMenu">
<items>
<MenuItem text="%main.directoryList.contextMenu.remove" onAction="#didClickRemoveSelectedEntry" />
<!-- TODO: -->
<MenuItem text="%main.directoryList.contextMenu.addUser" disable="true" />
<MenuItem text="%main.directoryList.contextMenu.changePassword" disable="true" />
</items>
</ContextMenu>
</fx:define> </fx:define>
<children> <children>
@@ -34,7 +44,7 @@
</VBox> </VBox>
<Pane fx:id="contentPane"> <Pane fx:id="contentPane">
<children> <children>
<fx:reference source="welcomeView"/> <fx:reference source="welcomeView" />
</children> </children>
</Pane> </Pane>
</children> </children>
+1 -1
View File
@@ -33,7 +33,7 @@
<Label fx:id="messageLabel" GridPane.rowIndex="0" GridPane.columnIndex="0" GridPane.columnSpan="2" /> <Label fx:id="messageLabel" GridPane.rowIndex="0" GridPane.columnIndex="0" GridPane.columnSpan="2" />
<!-- Row 1 --> <!-- Row 1 -->
<Button text="%unlocked.button.lock" defaultButton="true" GridPane.rowIndex="1" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="RIGHT" prefWidth="150.0" onAction="#closeVault" focusTraversable="false"/> <Button text="%unlocked.button.lock" defaultButton="true" GridPane.rowIndex="1" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="RIGHT" prefWidth="150.0" onAction="#didClickCloseVault" focusTraversable="false"/>
<!-- Row 2 --> <!-- Row 2 -->
<LineChart fx:id="ioGraph" GridPane.rowIndex="2" GridPane.columnIndex="0" GridPane.columnSpan="2" animated="false" createSymbols="false" prefHeight="300.0" legendVisible="true" legendSide="BOTTOM" verticalZeroLineVisible="false" verticalGridLinesVisible="false" horizontalGridLinesVisible="true"> <LineChart fx:id="ioGraph" GridPane.rowIndex="2" GridPane.columnIndex="0" GridPane.columnSpan="2" animated="false" createSymbols="false" prefHeight="300.0" legendVisible="true" legendSide="BOTTOM" verticalZeroLineVisible="false" verticalGridLinesVisible="false" horizontalGridLinesVisible="true">