From e2cdc517e404dd9c2631fe3ebefd0def95dc799e Mon Sep 17 00:00:00 2001
From: Manfred Karrer
Date: Tue, 7 Oct 2014 22:56:43 +0200
Subject: [PATCH] Use BlockdownloadPane from wallettemplate
---
.../btc/AddressBasedCoinSelector.java | 12 +-
.../java/io/bitsquare/btc/WalletFacade.java | 43 +++--
.../java/io/bitsquare/di/BitSquareModule.java | 4 +-
.../java/io/bitsquare/gui/main/MainModel.java | 48 ++---
.../java/io/bitsquare/gui/main/MainPM.java | 14 +-
.../io/bitsquare/gui/main/MainViewCB.java | 66 +++----
.../java/io/bitsquare/locale/BSResources.java | 2 +-
.../controls/NotificationBarPane.java | 142 ++++++++++++++
.../java/wallettemplate/utils/GuiUtils.java | 166 ++++++++++++++++
.../java/wallettemplate/utils/WTUtils.java | 87 +++++++++
.../utils/easing/EasingInterpolator.java | 134 +++++++++++++
.../utils/easing/EasingMode.java | 12 ++
.../utils/easing/ElasticInterpolator.java | 180 ++++++++++++++++++
src/main/resources/logback.xml | 6 +-
src/main/resources/wallet/checkpoints.testnet | Bin 0 -> 12885 bytes
15 files changed, 820 insertions(+), 96 deletions(-)
create mode 100644 src/main/java/wallettemplate/controls/NotificationBarPane.java
create mode 100644 src/main/java/wallettemplate/utils/GuiUtils.java
create mode 100644 src/main/java/wallettemplate/utils/WTUtils.java
create mode 100644 src/main/java/wallettemplate/utils/easing/EasingInterpolator.java
create mode 100644 src/main/java/wallettemplate/utils/easing/EasingMode.java
create mode 100644 src/main/java/wallettemplate/utils/easing/ElasticInterpolator.java
create mode 100644 src/main/resources/wallet/checkpoints.testnet
diff --git a/src/main/java/io/bitsquare/btc/AddressBasedCoinSelector.java b/src/main/java/io/bitsquare/btc/AddressBasedCoinSelector.java
index b1260cd2fc..16313344fb 100644
--- a/src/main/java/io/bitsquare/btc/AddressBasedCoinSelector.java
+++ b/src/main/java/io/bitsquare/btc/AddressBasedCoinSelector.java
@@ -95,14 +95,20 @@ class AddressBasedCoinSelector extends DefaultCoinSelector {
// TODO It might be risky to accept 0 confirmation tx from the network with only > 1 numBroadcastPeers
// Need to be tested in testnet and mainnet
// We need to handle cases when malleability happens or tx get lost and have not been successful propagated
- return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
+ /* return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
type.equals(TransactionConfidence.ConfidenceType.PENDING) &&
// we accept network tx without confirmations and numBroadcastPeers > 0
- /*confidence.getSource().equals(TransactionConfidence.Source.SELF) &&*/
+ //confidence.getSource().equals(TransactionConfidence.Source.SELF) &&
// In regtest mode we expect to have only one peer, so we won't see transactions propagate.
// TODO: The value 1 below dates from a time when transactions we broadcast *to* were
// counted, set to 0
- (confidence.numBroadcastPeers() > 1 || tx.getParams() == RegTestParams.get());
+ (confidence.numBroadcastPeers() > 1 || tx.getParams() == RegTestParams.get());*/
+
+ log.debug("numBroadcastPeers = " + confidence.numBroadcastPeers());
+ // TODO at testnet we got confidence.numBroadcastPeers()=1
+ return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
+ type.equals(TransactionConfidence.ConfidenceType.PENDING) &&
+ (confidence.numBroadcastPeers() > 0 || tx.getParams() == RegTestParams.get());
}
private static boolean isInBlockChain(Transaction tx) {
diff --git a/src/main/java/io/bitsquare/btc/WalletFacade.java b/src/main/java/io/bitsquare/btc/WalletFacade.java
index ef881adc26..32e87e8d5b 100644
--- a/src/main/java/io/bitsquare/btc/WalletFacade.java
+++ b/src/main/java/io/bitsquare/btc/WalletFacade.java
@@ -46,6 +46,7 @@ import org.bitcoinj.crypto.TransactionSignature;
import org.bitcoinj.kits.WalletAppKit;
import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.params.RegTestParams;
+import org.bitcoinj.params.TestNet3Params;
import org.bitcoinj.script.Script;
import org.bitcoinj.script.ScriptBuilder;
import org.bitcoinj.utils.Threading;
@@ -54,13 +55,13 @@ import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.Service;
import java.io.Serializable;
import java.math.BigInteger;
import java.util.ArrayList;
-import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
@@ -102,7 +103,7 @@ public class WalletFacade {
private final FeePolicy feePolicy;
private final CryptoFacade cryptoFacade;
private final Persistence persistence;
- private final List downloadListeners = new CopyOnWriteArrayList<>();
+ // private final List downloadListeners = new CopyOnWriteArrayList<>();
private final List addressConfidenceListeners = new CopyOnWriteArrayList<>();
private final List txConfidenceListeners = new CopyOnWriteArrayList<>();
private final List balanceListeners = new CopyOnWriteArrayList<>();
@@ -132,7 +133,7 @@ public class WalletFacade {
// Public Methods
///////////////////////////////////////////////////////////////////////////////////////////
- public void initialize(StartupListener startupListener) {
+ public void initialize(org.bitcoinj.core.DownloadListener downloadListener, StartupListener startupListener) {
// Tell bitcoinj to execute event handlers on the JavaFX UI thread. This keeps things simple and means
// we cannot forget to switch threads when adding event handlers. Unfortunately, the DownloadListener
// we give to the app kit is currently an exception and runs on a library thread. It'll get fixed in
@@ -164,7 +165,7 @@ public class WalletFacade {
// Checkpoint files are made using the BuildCheckpoints tool and usually we have to download the
// last months worth or more (takes a few seconds).
try {
- walletAppKit.setCheckpoints(getClass().getClassLoader().getResourceAsStream("wallet/checkpoints"));
+ walletAppKit.setCheckpoints(getClass().getResourceAsStream("wallet/checkpoints"));
} catch (Exception e) {
e.printStackTrace();
log.error(e.toString());
@@ -172,24 +173,40 @@ public class WalletFacade {
// As an example!
// walletAppKit.useTor();
}
- walletAppKit.setDownloadListener(new BlockChainDownloadListener())
+ else if (params == TestNet3Params.get()) {
+ walletAppKit.setCheckpoints(getClass().getResourceAsStream("wallet/checkpoints.testnet"));
+ //walletAppKit.useTor();
+ }
+ walletAppKit.setDownloadListener(downloadListener)
.setBlockingStartup(false)
- .restoreWalletFromSeed(null)
.setUserAgent("BitSquare", "0.1");
+
+ /*
+ // TODO restore from DeterministicSeed
+ if (seed != null)
+ walletAppKit.restoreWalletFromSeed(seed);
+ */
+ walletAppKit.addListener(new Service.Listener() {
+ @Override
+ public void failed(Service.State from, Throwable failure) {
+ walletAppKit = null;
+ // TODO show error popup
+ //crashAlert(failure);
+ }
+ }, Threading.SAME_THREAD);
walletAppKit.startAsync();
}
private void initWallet() {
wallet = walletAppKit.wallet();
- wallet.allowSpendingUnconfirmedTransactions();
//walletAppKit.peerGroup().setMaxConnections(11);
- if (params == RegTestParams.get())
+ /* if (params == RegTestParams.get())
walletAppKit.peerGroup().setMinBroadcastConnections(1);
else
- walletAppKit.peerGroup().setMinBroadcastConnections(3);
+ walletAppKit.peerGroup().setMinBroadcastConnections(3);*/
walletEventListener = new WalletEventListener() {
@@ -266,14 +283,14 @@ public class WalletFacade {
// Listener
///////////////////////////////////////////////////////////////////////////////////////////
- public DownloadListener addDownloadListener(DownloadListener listener) {
+ /* public DownloadListener addDownloadListener(DownloadListener listener) {
downloadListeners.add(listener);
return listener;
}
public void removeDownloadListener(DownloadListener listener) {
downloadListeners.remove(listener);
- }
+ }*/
public AddressConfidenceListener addAddressConfidenceListener(AddressConfidenceListener listener) {
addressConfidenceListeners.add(listener);
@@ -1140,7 +1157,7 @@ public class WalletFacade {
void downloadComplete();
}
- private class BlockChainDownloadListener extends org.bitcoinj.core.DownloadListener {
+ /* private class BlockChainDownloadListener extends org.bitcoinj.core.DownloadListener {
@Override
protected void progress(double percent, int blocksSoFar, Date date) {
super.progress(percent, blocksSoFar, date);
@@ -1164,6 +1181,6 @@ public class WalletFacade {
downloadListener.downloadComplete();
}
}
- }
+ }*/
}
diff --git a/src/main/java/io/bitsquare/di/BitSquareModule.java b/src/main/java/io/bitsquare/di/BitSquareModule.java
index 93849519be..8f5bebf79d 100644
--- a/src/main/java/io/bitsquare/di/BitSquareModule.java
+++ b/src/main/java/io/bitsquare/di/BitSquareModule.java
@@ -146,8 +146,8 @@ class NetworkParametersProvider implements Provider {
// Set default
// String networkType= WalletFacade.MAIN_NET;
- // String networkType = WalletFacade.TEST_NET;
- String networkType = WalletFacade.REG_TEST_NET;
+ String networkType = WalletFacade.TEST_NET;
+ // String networkType = WalletFacade.REG_TEST_NET;
if (networkTypeFromConfig != null)
networkType = networkTypeFromConfig;
diff --git a/src/main/java/io/bitsquare/gui/main/MainModel.java b/src/main/java/io/bitsquare/gui/main/MainModel.java
index 145835c57e..27a054dc3f 100644
--- a/src/main/java/io/bitsquare/gui/main/MainModel.java
+++ b/src/main/java/io/bitsquare/gui/main/MainModel.java
@@ -30,8 +30,13 @@ import io.bitsquare.trade.Trade;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.user.User;
+import org.bitcoinj.core.DownloadListener;
+
import com.google.inject.Inject;
+import java.util.Date;
+
+import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
@@ -60,8 +65,6 @@ class MainModel extends UIModel {
final BooleanProperty backendInited = new SimpleBooleanProperty();
final DoubleProperty networkSyncProgress = new SimpleDoubleProperty();
- final BooleanProperty networkSyncComplete = new SimpleBooleanProperty();
- // final ObjectProperty balance = new SimpleObjectProperty<>();
final IntegerProperty numPendingTrades = new SimpleIntegerProperty(0);
///////////////////////////////////////////////////////////////////////////////////////////
@@ -142,19 +145,24 @@ class MainModel extends UIModel {
Profiler.printMsgWithTime("MainModel.initFacades");
- walletFacade.initialize(() -> {
+ DownloadListener downloadListener = new DownloadListener() {
+ @Override
+ protected void progress(double percent, int blocksLeft, Date date) {
+ super.progress(percent, blocksLeft, date);
+ Platform.runLater(() -> networkSyncProgress.set(percent / 100.0));
+ }
+
+ @Override
+ protected void doneDownload() {
+ super.doneDownload();
+ Platform.runLater(() -> networkSyncProgress.set(1.0));
+ }
+ };
+
+ walletFacade.initialize(downloadListener, () -> {
walletFacadeInited = true;
if (messageFacadeInited)
onFacadesInitialised();
-
-
- /* walletFacade.addBalanceListener(new BalanceListener() {
- @Override
- public void onBalanceChanged(Coin balance) {
- updateBalance(balance);
- }
- });
- updateBalance(walletFacade.getWalletBalance());*/
});
}
@@ -187,19 +195,6 @@ class MainModel extends UIModel {
///////////////////////////////////////////////////////////////////////////////////////////
private void onFacadesInitialised() {
- // TODO Consider to use version sync notification pane from Mike Hearn
- walletFacade.addDownloadListener(new WalletFacade.DownloadListener() {
- @Override
- public void progress(double percent) {
- networkSyncProgress.set(percent);
- }
-
- @Override
- public void downloadComplete() {
- networkSyncComplete.set(true);
- }
- });
-
tradeManager.getPendingTrades().addListener((MapChangeListener) change -> updateNumPendingTrades());
updateNumPendingTrades();
@@ -211,7 +206,4 @@ class MainModel extends UIModel {
numPendingTrades.set(tradeManager.getPendingTrades().size());
}
- /* private void updateBalance(Coin balance) {
- this.balance.set(balance);
- }*/
}
diff --git a/src/main/java/io/bitsquare/gui/main/MainPM.java b/src/main/java/io/bitsquare/gui/main/MainPM.java
index fe4903d49a..cdc3f2c22c 100644
--- a/src/main/java/io/bitsquare/gui/main/MainPM.java
+++ b/src/main/java/io/bitsquare/gui/main/MainPM.java
@@ -24,9 +24,11 @@ import io.bitsquare.gui.util.BSFormatter;
import com.google.inject.Inject;
import javafx.beans.property.BooleanProperty;
+import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
+import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
@@ -40,15 +42,14 @@ import org.slf4j.LoggerFactory;
class MainPM extends PresentationModel {
private static final Logger log = LoggerFactory.getLogger(MainPM.class);
+ private BSFormatter formatter;
+
final BooleanProperty backendInited = new SimpleBooleanProperty();
- // final StringProperty balance = new SimpleStringProperty();
final StringProperty bankAccountsComboBoxPrompt = new SimpleStringProperty();
final BooleanProperty bankAccountsComboBoxDisable = new SimpleBooleanProperty();
final StringProperty splashScreenInfoText = new SimpleStringProperty();
- final BooleanProperty networkSyncComplete = new SimpleBooleanProperty();
final IntegerProperty numPendingTrades = new SimpleIntegerProperty();
- private BSFormatter formatter;
-
+ final DoubleProperty networkSyncProgress = new SimpleDoubleProperty();
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
@@ -71,7 +72,7 @@ class MainPM extends PresentationModel {
super.initialize();
backendInited.bind(model.backendInited);
- networkSyncComplete.bind(model.networkSyncComplete);
+ networkSyncProgress.bind(model.networkSyncProgress);
numPendingTrades.bind(model.numPendingTrades);
model.networkSyncProgress.addListener((ov, oldValue, newValue) -> {
@@ -84,9 +85,6 @@ class MainPM extends PresentationModel {
});
- /*model.balance.addListener((ov, oldValue, newValue) -> balance.set(formatter.formatCoinWithCode
- (newValue)));*/
-
model.getBankAccounts().addListener((ListChangeListener) change -> {
bankAccountsComboBoxDisable.set(change.getList().isEmpty());
bankAccountsComboBoxPrompt.set(change.getList().isEmpty() ? "No accounts" : "");
diff --git a/src/main/java/io/bitsquare/gui/main/MainViewCB.java b/src/main/java/io/bitsquare/gui/main/MainViewCB.java
index bc361d666d..67b863ae55 100644
--- a/src/main/java/io/bitsquare/gui/main/MainViewCB.java
+++ b/src/main/java/io/bitsquare/gui/main/MainViewCB.java
@@ -52,6 +52,8 @@ import javafx.scene.paint.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import wallettemplate.controls.NotificationBarPane;
+
public class MainViewCB extends ViewCB {
private static final Logger log = LoggerFactory.getLogger(MainViewCB.class);
@@ -61,7 +63,7 @@ public class MainViewCB extends ViewCB {
private final ToggleGroup navButtonsGroup = new ToggleGroup();
private BorderPane baseApplicationContainer;
- private VBox baseOverlayContainer;
+ private StackPane baseOverlayContainer;
private AnchorPane contentContainer;
private HBox leftNavPane, rightNavPane;
private NetworkSyncPane networkSyncPane;
@@ -69,6 +71,7 @@ public class MainViewCB extends ViewCB {
accountButton;
private Pane ordersButtonButtonPane;
private Label numPendingTradesLabel;
+ private NotificationBarPane notificationBarPane;
///////////////////////////////////////////////////////////////////////////////////////////
@@ -166,10 +169,24 @@ public class MainViewCB extends ViewCB {
private void startup() {
baseApplicationContainer = getBaseApplicationContainer();
- baseOverlayContainer = getSplashScreen();
- ((StackPane) root).getChildren().addAll(baseApplicationContainer, baseOverlayContainer);
+ baseOverlayContainer = new StackPane();
- onBaseContainersCreated();
+ // TODO remove dependency of NotificationBarPane with getSplashScreen (borderPane content)
+ notificationBarPane = new NotificationBarPane(getSplashScreen());
+ baseOverlayContainer.getChildren().add(notificationBarPane);
+
+ final NotificationBarPane.Item syncItem = notificationBarPane.pushItem("Synchronising with the Bitcoin network",
+ presentationModel.networkSyncProgress);
+
+ presentationModel.networkSyncProgress.addListener((ov, oldValue, newValue) -> {
+ if ((double) newValue >= 1.0) {
+ log.debug("### networkSyncProgress " + newValue);
+ syncItem.cancel();
+ onBaseContainersCreated();
+ }
+ });
+
+ ((StackPane) root).getChildren().addAll(baseApplicationContainer, baseOverlayContainer);
}
private void onBaseContainersCreated() {
@@ -284,7 +301,7 @@ public class MainViewCB extends ViewCB {
return borderPane;
}
- private VBox getSplashScreen() {
+ private BorderPane getSplashScreen() {
VBox vBox = new VBox();
vBox.setAlignment(Pos.CENTER);
vBox.setSpacing(10);
@@ -303,7 +320,9 @@ public class MainViewCB extends ViewCB {
loadingLabel.textProperty().bind(presentationModel.splashScreenInfoText);
vBox.getChildren().addAll(logo, subTitle, loadingLabel);
- return vBox;
+
+ BorderPane borderPane = new BorderPane(vBox);
+ return borderPane;
}
private AnchorPane getApplicationContainer() {
@@ -333,15 +352,6 @@ public class MainViewCB extends ViewCB {
AnchorPane.setLeftAnchor(networkSyncPane, 0d);
AnchorPane.setBottomAnchor(networkSyncPane, 5d);
- // TODO sometimes it keeps running... deactivate ti for the moment and replace it with the notification pane
- // from Mike Hearn later
- networkSyncPane.setVisible(false);
-
- presentationModel.networkSyncComplete.addListener((ov, old, newValue) -> {
- if (newValue)
- networkSyncPane.downloadComplete();
- });
-
anchorPane.getChildren().addAll(leftNavPane, rightNavPane, contentContainer, networkSyncPane);
return anchorPane;
}
@@ -361,8 +371,6 @@ public class MainViewCB extends ViewCB {
msgButton = addNavButton(msgButtonHolder, "Messages", Navigation.Item.MSG);
leftNavPane.getChildren().add(msgButtonHolder);
- //addBalanceInfo(rightNavPane);
-
addBankAccountComboBox(rightNavPane);
settingsButton = addNavButton(rightNavPane, "Preferences", Navigation.Item.SETTINGS);
@@ -408,28 +416,6 @@ public class MainViewCB extends ViewCB {
return toggleButton;
}
- /*private void addBalanceInfo(Pane parent) {
- final TextField balanceTextField = new TextField();
- balanceTextField.setEditable(false);
- balanceTextField.setPrefWidth(110);
- balanceTextField.setId("nav-balance-label");
- balanceTextField.textProperty().bind(presentationModel.balance);
-
-
- final Label titleLabel = new Label("Balance");
- titleLabel.setMouseTransparent(true);
- titleLabel.setId("nav-button-label");
- balanceTextField.widthProperty().addListener((ov, o, n) ->
- titleLabel.setLayoutX(((double) n - titleLabel.getWidth()) / 2));
-
- final VBox vBox = new VBox();
- vBox.setPadding(new Insets(12, 5, 0, 0));
- vBox.setSpacing(2);
- vBox.getChildren().setAll(balanceTextField, titleLabel);
- vBox.setAlignment(Pos.CENTER);
- parent.getChildren().add(vBox);
- }*/
-
private void addBankAccountComboBox(Pane parent) {
final ComboBox comboBox = new ComboBox<>(presentationModel.getBankAccounts());
comboBox.setLayoutY(12);
@@ -460,4 +446,4 @@ public class MainViewCB extends ViewCB {
vBox.getChildren().setAll(comboBox, titleLabel);
parent.getChildren().add(vBox);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/io/bitsquare/locale/BSResources.java b/src/main/java/io/bitsquare/locale/BSResources.java
index 216b1b61c5..386c02e791 100644
--- a/src/main/java/io/bitsquare/locale/BSResources.java
+++ b/src/main/java/io/bitsquare/locale/BSResources.java
@@ -48,7 +48,7 @@ public class BSResources {
try {
return BSResources.getResourceBundle().getString(key);
} catch (MissingResourceException e) {
- log.warn("MissingResourceException for key: " + key);
+ log.warn("Missing resource for key: " + key);
return key;
}
}
diff --git a/src/main/java/wallettemplate/controls/NotificationBarPane.java b/src/main/java/wallettemplate/controls/NotificationBarPane.java
new file mode 100644
index 0000000000..47024ab892
--- /dev/null
+++ b/src/main/java/wallettemplate/controls/NotificationBarPane.java
@@ -0,0 +1,142 @@
+package wallettemplate.controls;
+
+
+import javax.annotation.Nullable;
+
+import javafx.animation.Interpolator;
+import javafx.animation.KeyFrame;
+import javafx.animation.KeyValue;
+import javafx.animation.Timeline;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.value.ObservableDoubleValue;
+import javafx.collections.FXCollections;
+import javafx.collections.ListChangeListener;
+import javafx.collections.ObservableList;
+import javafx.scene.*;
+import javafx.scene.control.*;
+import javafx.scene.layout.*;
+import javafx.util.Duration;
+
+import wallettemplate.utils.GuiUtils;
+import wallettemplate.utils.easing.EasingMode;
+import wallettemplate.utils.easing.ElasticInterpolator;
+
+/**
+ * Wraps the given Node in a BorderPane and allows a thin bar to slide in from the bottom or top, squeezing the content
+ * node. The API allows different "items" to be added/removed and they will be displayed one at a time, fading between
+ * them when the topmost is removed. Each item is meant to be used for e.g. a background task and can contain a button
+ * and/or a progress bar.
+ */
+public class NotificationBarPane extends BorderPane {
+ public static final Duration ANIM_IN_DURATION = GuiUtils.UI_ANIMATION_TIME.multiply(2);
+ public static final Duration ANIM_OUT_DURATION = GuiUtils.UI_ANIMATION_TIME;
+
+ private HBox bar;
+ private Label label;
+ private double barHeight;
+ private ProgressBar progressBar;
+
+ public class Item {
+ public final SimpleStringProperty label;
+ @Nullable public final ObservableDoubleValue progress;
+
+ public Item(String label, @Nullable ObservableDoubleValue progress) {
+ this.label = new SimpleStringProperty(label);
+ this.progress = progress;
+ }
+
+ public void cancel() {
+ items.remove(this);
+ }
+ }
+
+ public final ObservableList- items;
+
+ public NotificationBarPane(Node content) {
+ super(content);
+ progressBar = new ProgressBar();
+ label = new Label("infobar!");
+ bar = new HBox(label);
+ bar.setMinHeight(0.0);
+ bar.getStyleClass().add("info-bar");
+ bar.setFillHeight(true);
+ setBottom(bar);
+ // Figure out the height of the bar based on the CSS. Must wait until after we've been added to the parent node.
+ sceneProperty().addListener(o -> {
+ if (getParent() == null) return;
+ getParent().applyCss();
+ getParent().layout();
+ barHeight = bar.getHeight();
+ bar.setPrefHeight(0.0);
+ });
+ items = FXCollections.observableArrayList();
+ items.addListener((ListChangeListener super Item>) change -> {
+ config();
+ showOrHide();
+ });
+ }
+
+ private void config() {
+ if (items.isEmpty()) return;
+ Item item = items.get(0);
+
+ bar.getChildren().clear();
+ label.textProperty().bind(item.label);
+ label.setMaxWidth(Double.MAX_VALUE);
+ HBox.setHgrow(label, Priority.ALWAYS);
+ bar.getChildren().add(label);
+ if (item.progress != null) {
+ progressBar.setMinWidth(200);
+ progressBar.progressProperty().bind(item.progress);
+ bar.getChildren().add(progressBar);
+ }
+ }
+
+ private void showOrHide() {
+ if (items.isEmpty())
+ animateOut();
+ else
+ animateIn();
+ }
+
+ public boolean isShowing() {
+ return bar.getPrefHeight() > 0;
+ }
+
+ private void animateIn() {
+ animate(barHeight);
+ }
+
+ private void animateOut() {
+ animate(0.0);
+ }
+
+ private Timeline timeline;
+
+ protected void animate(Number target) {
+ if (timeline != null) {
+ timeline.stop();
+ timeline = null;
+ }
+ Duration duration;
+ Interpolator interpolator;
+ if (target.intValue() > 0) {
+ interpolator = new ElasticInterpolator(EasingMode.EASE_OUT, 1, 2);
+ duration = ANIM_IN_DURATION;
+ }
+ else {
+ interpolator = Interpolator.EASE_OUT;
+ duration = ANIM_OUT_DURATION;
+ }
+ KeyFrame kf = new KeyFrame(duration, new KeyValue(bar.prefHeightProperty(), target, interpolator));
+ timeline = new Timeline(kf);
+ timeline.setOnFinished(x -> timeline = null);
+ timeline.play();
+ }
+
+ public Item pushItem(String string, @Nullable ObservableDoubleValue progress) {
+ Item i = new Item(string, progress);
+ items.add(i);
+ return i;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/wallettemplate/utils/GuiUtils.java b/src/main/java/wallettemplate/utils/GuiUtils.java
new file mode 100644
index 0000000000..1aaed136ff
--- /dev/null
+++ b/src/main/java/wallettemplate/utils/GuiUtils.java
@@ -0,0 +1,166 @@
+package wallettemplate.utils;
+
+import javafx.animation.Animation;
+import javafx.animation.FadeTransition;
+import javafx.animation.KeyFrame;
+import javafx.animation.KeyValue;
+import javafx.animation.ScaleTransition;
+import javafx.animation.Timeline;
+import javafx.application.Platform;
+import javafx.scene.*;
+import javafx.scene.effect.*;
+import javafx.scene.layout.*;
+import javafx.util.Duration;
+
+import static com.google.common.base.Preconditions.checkState;
+
+public class GuiUtils {
+ /*public static void runAlert(BiConsumer setup) {
+ try {
+ // JavaFX2 doesn't actually have a standard alert template. Instead the Scene Builder app will create FXML
+ // files for an alert window for you, and then you customise it as you see fit. I guess it makes sense in
+ // an odd sort of way.
+ Stage dialogStage = new Stage();
+ dialogStage.initModality(Modality.APPLICATION_MODAL);
+ FXMLLoader loader = new FXMLLoader(GuiUtils.class.getResource("alert.fxml"));
+ Pane pane = loader.load();
+ AlertWindowController controller = loader.getController();
+ setup.accept(dialogStage, controller);
+ dialogStage.setScene(new Scene(pane));
+ dialogStage.showAndWait();
+ } catch (IOException e) {
+ // We crashed whilst trying to show the alert dialog (this should never happen). Give up!
+ throw new RuntimeException(e);
+ }
+ }*/
+
+ /* public static void crashAlert(Throwable t) {
+ t.printStackTrace();
+ Throwable rootCause = Throwables.getRootCause(t);
+ Runnable r = () -> {
+ runAlert((stage, controller) -> controller.crashAlert(stage, rootCause.toString()));
+ Platform.exit();
+ };
+ if (Platform.isFxApplicationThread())
+ r.run();
+ else
+ Platform.runLater(r);
+ }*/
+
+ /**
+ * Show a GUI alert box for any unhandled exceptions that propagate out of this thread.
+ */
+ /*public static void handleCrashesOnThisThread() {
+ Thread.currentThread().setUncaughtExceptionHandler((thread, exception) -> {
+ GuiUtils.crashAlert(Throwables.getRootCause(exception));
+ });
+ }
+
+ public static void informationalAlert(String message, String details, Object... args) {
+ String formattedDetails = String.format(details, args);
+ Runnable r = () -> runAlert((stage, controller) -> controller.informational(stage, message, formattedDetails));
+ if (Platform.isFxApplicationThread())
+ r.run();
+ else
+ Platform.runLater(r);
+ }*/
+
+ public static final int UI_ANIMATION_TIME_MSEC = 600;
+ public static final Duration UI_ANIMATION_TIME = Duration.millis(UI_ANIMATION_TIME_MSEC);
+
+ public static Animation fadeIn(Node ui) {
+ return fadeIn(ui, 0);
+ }
+
+ public static Animation fadeIn(Node ui, int delayMillis) {
+ ui.setCache(true);
+ FadeTransition ft = new FadeTransition(Duration.millis(UI_ANIMATION_TIME_MSEC), ui);
+ ft.setFromValue(0.0);
+ ft.setToValue(1.0);
+ ft.setOnFinished(ev -> ui.setCache(false));
+ ft.setDelay(Duration.millis(delayMillis));
+ ft.play();
+ return ft;
+ }
+
+ public static Animation fadeOut(Node ui) {
+ FadeTransition ft = new FadeTransition(Duration.millis(UI_ANIMATION_TIME_MSEC), ui);
+ ft.setFromValue(ui.getOpacity());
+ ft.setToValue(0.0);
+ ft.play();
+ return ft;
+ }
+
+ public static Animation fadeOutAndRemove(Pane parentPane, Node... nodes) {
+ Animation animation = fadeOut(nodes[0]);
+ animation.setOnFinished(actionEvent -> parentPane.getChildren().removeAll(nodes));
+ return animation;
+ }
+
+ public static Animation fadeOutAndRemove(Duration duration, Pane parentPane, Node... nodes) {
+ nodes[0].setCache(true);
+ FadeTransition ft = new FadeTransition(duration, nodes[0]);
+ ft.setFromValue(nodes[0].getOpacity());
+ ft.setToValue(0.0);
+ ft.setOnFinished(actionEvent -> parentPane.getChildren().removeAll(nodes));
+ ft.play();
+ return ft;
+ }
+
+ public static void blurOut(Node node) {
+ GaussianBlur blur = new GaussianBlur(0.0);
+ node.setEffect(blur);
+ Timeline timeline = new Timeline();
+ KeyValue kv = new KeyValue(blur.radiusProperty(), 10.0);
+ KeyFrame kf = new KeyFrame(Duration.millis(UI_ANIMATION_TIME_MSEC), kv);
+ timeline.getKeyFrames().add(kf);
+ timeline.play();
+ }
+
+ public static void blurIn(Node node) {
+ GaussianBlur blur = (GaussianBlur) node.getEffect();
+ Timeline timeline = new Timeline();
+ KeyValue kv = new KeyValue(blur.radiusProperty(), 0.0);
+ KeyFrame kf = new KeyFrame(Duration.millis(UI_ANIMATION_TIME_MSEC), kv);
+ timeline.getKeyFrames().add(kf);
+ timeline.setOnFinished(actionEvent -> node.setEffect(null));
+ timeline.play();
+ }
+
+ public static ScaleTransition zoomIn(Node node) {
+ return zoomIn(node, 0);
+ }
+
+ public static ScaleTransition zoomIn(Node node, int delayMillis) {
+ return scaleFromTo(node, 0.95, 1.0, delayMillis);
+ }
+
+ public static ScaleTransition explodeOut(Node node) {
+ return scaleFromTo(node, 1.0, 1.05, 0);
+ }
+
+ private static ScaleTransition scaleFromTo(Node node, double from, double to, int delayMillis) {
+ ScaleTransition scale = new ScaleTransition(Duration.millis(UI_ANIMATION_TIME_MSEC / 2), node);
+ scale.setFromX(from);
+ scale.setFromY(from);
+ scale.setToX(to);
+ scale.setToY(to);
+ scale.setDelay(Duration.millis(delayMillis));
+ scale.play();
+ return scale;
+ }
+
+ /**
+ * A useful helper for development purposes. Used as a switch for loading files from local disk, allowing live
+ * editing whilst the app runs without rebuilds.
+ */
+ /* public static URL getResource(String name) {
+ if (false)
+ return unchecked(() -> new URL("file:///your/path/here/src/main/wallettemplate/" + name));
+ else
+ return MainController.class.getResource(name);
+ }*/
+ public static void checkGuiThread() {
+ checkState(Platform.isFxApplicationThread());
+ }
+}
diff --git a/src/main/java/wallettemplate/utils/WTUtils.java b/src/main/java/wallettemplate/utils/WTUtils.java
new file mode 100644
index 0000000000..2293f926d8
--- /dev/null
+++ b/src/main/java/wallettemplate/utils/WTUtils.java
@@ -0,0 +1,87 @@
+/*
+ * This file is part of Bitsquare.
+ *
+ * Bitsquare is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bitsquare is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bitsquare. If not, see .
+ */
+
+package wallettemplate.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Some generic utilities to make Java a bit less annoying.
+ */
+public class WTUtils {
+ private static final Logger log = LoggerFactory.getLogger(WTUtils.class);
+
+ public interface UncheckedRun {
+ public T run() throws Throwable;
+ }
+
+ public interface UncheckedRunnable {
+ public void run() throws Throwable;
+ }
+
+ public static T unchecked(UncheckedRun run) {
+ try {
+ return run.run();
+ } catch (Throwable throwable) {
+ throw new RuntimeException(throwable);
+ }
+ }
+
+ public static void uncheck(UncheckedRunnable run) {
+ try {
+ run.run();
+ } catch (Throwable throwable) {
+ throw new RuntimeException(throwable);
+ }
+ }
+
+ public static void ignoreAndLog(UncheckedRunnable runnable) {
+ try {
+ runnable.run();
+ } catch (Throwable t) {
+ log.error("Ignoring error", t);
+ }
+ }
+
+ public static T ignoredAndLogged(UncheckedRun runnable) {
+ try {
+ return runnable.run();
+ } catch (Throwable t) {
+ log.error("Ignoring error", t);
+ return null;
+ }
+ }
+
+ public static boolean didThrow(UncheckedRun run) {
+ try {
+ run.run();
+ return false;
+ } catch (Throwable throwable) {
+ return true;
+ }
+ }
+
+ public static boolean didThrow(UncheckedRunnable run) {
+ try {
+ run.run();
+ return false;
+ } catch (Throwable throwable) {
+ return true;
+ }
+ }
+}
diff --git a/src/main/java/wallettemplate/utils/easing/EasingInterpolator.java b/src/main/java/wallettemplate/utils/easing/EasingInterpolator.java
new file mode 100644
index 0000000000..5c2adef59c
--- /dev/null
+++ b/src/main/java/wallettemplate/utils/easing/EasingInterpolator.java
@@ -0,0 +1,134 @@
+/*
+ * This file is part of Bitsquare.
+ *
+ * Bitsquare is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or (at
+ * your option) any later version.
+ *
+ * Bitsquare is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with Bitsquare. If not, see .
+ */
+
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, Christian Schudt
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package wallettemplate.utils.easing;
+
+import javafx.animation.Interpolator;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+
+/**
+ * The abstract base class for all easing interpolators.
+ *
+ * @author Christian Schudt
+ */
+public abstract class EasingInterpolator extends Interpolator {
+
+ /**
+ * The easing mode.
+ */
+ private ObjectProperty easingMode = new SimpleObjectProperty<>(EasingMode.EASE_OUT);
+
+ /**
+ * Constructs the interpolator with a specific easing mode.
+ *
+ * @param easingMode The easing mode.
+ */
+ public EasingInterpolator(EasingMode easingMode) {
+ this.easingMode.set(easingMode);
+ }
+
+ /**
+ * The easing mode property.
+ *
+ * @return The property.
+ * @see #getEasingMode()
+ * @see #setEasingMode(EasingMode)
+ */
+ public ObjectProperty easingModeProperty() {
+ return easingMode;
+ }
+
+ /**
+ * Gets the easing mode.
+ *
+ * @return The easing mode.
+ * @see #easingModeProperty()
+ */
+ public EasingMode getEasingMode() {
+ return easingMode.get();
+ }
+
+ /**
+ * Sets the easing mode.
+ *
+ * @param easingMode The easing mode.
+ * @see #easingModeProperty()
+ */
+ public void setEasingMode(EasingMode easingMode) {
+ this.easingMode.set(easingMode);
+ }
+
+ /**
+ * Defines the base curve for the interpolator.
+ * The base curve is then transformed into an easing-in, easing-out easing-both curve.
+ *
+ * @param v The normalized value/time/progress of the interpolation (between 0 and 1).
+ * @return The resulting value of the function, should return a value between 0 and 1.
+ * @see javafx.animation.Interpolator#curve(double)
+ */
+ protected abstract double baseCurve(final double v);
+
+ /**
+ * Curves the function depending on the easing mode.
+ *
+ * @param v The normalized value (between 0 and 1).
+ * @return The resulting value of the function.
+ */
+ @Override
+ protected final double curve(final double v) {
+ switch (easingMode.get()) {
+ case EASE_IN:
+ return baseCurve(v);
+ case EASE_OUT:
+ return 1 - baseCurve(1 - v);
+ case EASE_BOTH:
+ if (v <= 0.5) {
+ return baseCurve(2 * v) / 2;
+ }
+ else {
+ return (2 - baseCurve(2 * (1 - v))) / 2;
+ }
+
+ }
+ return baseCurve(v);
+ }
+}
diff --git a/src/main/java/wallettemplate/utils/easing/EasingMode.java b/src/main/java/wallettemplate/utils/easing/EasingMode.java
new file mode 100644
index 0000000000..46b29bbe12
--- /dev/null
+++ b/src/main/java/wallettemplate/utils/easing/EasingMode.java
@@ -0,0 +1,12 @@
+package wallettemplate.utils.easing;
+
+/**
+ * Defines the three easing modes, ease-in, ease-out and ease-both.
+ *
+ * @author Christian Schudt
+ */
+public enum EasingMode {
+ EASE_IN,
+ EASE_OUT,
+ EASE_BOTH
+}
diff --git a/src/main/java/wallettemplate/utils/easing/ElasticInterpolator.java b/src/main/java/wallettemplate/utils/easing/ElasticInterpolator.java
new file mode 100644
index 0000000000..f627f217c0
--- /dev/null
+++ b/src/main/java/wallettemplate/utils/easing/ElasticInterpolator.java
@@ -0,0 +1,180 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2013, Christian Schudt
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+package wallettemplate.utils.easing;
+
+import javafx.beans.property.DoubleProperty;
+import javafx.beans.property.SimpleDoubleProperty;
+
+/**
+ * This interpolator simulates an elastic behavior.
+ *
+ * The following curve illustrates the interpolation.
+ *
+ *
+ *
+ * The math in this class is taken from
+ * http://www.robertpenner.com/easing/.
+ *
+ * @author Christian Schudt
+ */
+public class ElasticInterpolator extends EasingInterpolator {
+
+ /**
+ * The amplitude.
+ */
+ private DoubleProperty amplitude = new SimpleDoubleProperty(this, "amplitude", 1);
+
+ /**
+ * The number of oscillations.
+ */
+ private DoubleProperty oscillations = new SimpleDoubleProperty(this, "oscillations", 3);
+
+ /**
+ * Default constructor. Initializes the interpolator with ease out mode.
+ */
+ public ElasticInterpolator() {
+ this(EasingMode.EASE_OUT);
+ }
+
+ /**
+ * Constructs the interpolator with a specific easing mode.
+ *
+ * @param easingMode The easing mode.
+ */
+ public ElasticInterpolator(EasingMode easingMode) {
+ super(easingMode);
+ }
+
+ /**
+ * Sets the easing mode.
+ *
+ * @param easingMode The easing mode.
+ * @see #easingModeProperty()
+ */
+ public ElasticInterpolator(EasingMode easingMode, double amplitude, double oscillations) {
+ super(easingMode);
+ this.amplitude.set(amplitude);
+ this.oscillations.set(oscillations);
+ }
+
+ /**
+ * The oscillations property. Defines number of oscillations.
+ *
+ * @return The property.
+ * @see #getOscillations()
+ * @see #setOscillations(double)
+ */
+ public DoubleProperty oscillationsProperty() {
+ return oscillations;
+ }
+
+ /**
+ * The amplitude. The minimum value is 1. If this value is < 1 it will be set to 1 during animation.
+ *
+ * @return The property.
+ * @see #getAmplitude()
+ * @see #setAmplitude(double)
+ */
+ public DoubleProperty amplitudeProperty() {
+ return amplitude;
+ }
+
+ /**
+ * Gets the amplitude.
+ *
+ * @return The amplitude.
+ * @see #amplitudeProperty()
+ */
+ public double getAmplitude() {
+ return amplitude.get();
+ }
+
+ /**
+ * Sets the amplitude.
+ *
+ * @param amplitude The amplitude.
+ * @see #amplitudeProperty()
+ */
+ public void setAmplitude(final double amplitude) {
+ this.amplitude.set(amplitude);
+ }
+
+ /**
+ * Gets the number of oscillations.
+ *
+ * @return The oscillations.
+ * @see #oscillationsProperty()
+ */
+ public double getOscillations() {
+ return oscillations.get();
+ }
+
+ /**
+ * Sets the number of oscillations.
+ *
+ * @param oscillations The oscillations.
+ * @see #oscillationsProperty()
+ */
+ public void setOscillations(final double oscillations) {
+ this.oscillations.set(oscillations);
+ }
+
+ @Override
+ protected double baseCurve(double v) {
+ if (v == 0) {
+ return 0;
+ }
+ if (v == 1) {
+ return 1;
+ }
+ double p = 1.0 / oscillations.get();
+ double a = amplitude.get();
+ double s;
+ if (a < Math.abs(1)) {
+ a = 1;
+ s = p / 4;
+ }
+ else {
+ s = p / (2 * Math.PI) * Math.asin(1 / a);
+ }
+ return -(a * Math.pow(2, 10 * (v -= 1)) * Math.sin((v - s) * (2 * Math.PI) / p));
+ }
+}
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
index 75ed2bdde6..ddc54c46ec 100644
--- a/src/main/resources/logback.xml
+++ b/src/main/resources/logback.xml
@@ -25,7 +25,7 @@
-
+
@@ -36,6 +36,10 @@
+
+
+
+