package wallettemplate; import com.subgraph.orchid.TorClient; import com.subgraph.orchid.TorInitializationListener; import javafx.animation.TranslateTransition; import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.ListView; import javafx.scene.control.TabPane; import javafx.scene.control.cell.TextFieldListCell; import javafx.scene.layout.HBox; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.util.Duration; import javafx.util.StringConverter; import network.thunder.core.communication.objects.messages.impl.message.gossip.objects.PubkeyIPObject; import network.thunder.core.communication.objects.messages.impl.results.NullResultCommand; import org.bitcoinj.core.Address; import org.bitcoinj.core.Coin; import org.bitcoinj.core.DownloadProgressTracker; import org.bitcoinj.core.Transaction; import org.bitcoinj.utils.MonetaryFormat; import org.fxmisc.easybind.EasyBind; import wallettemplate.controls.NotificationBarPane; import wallettemplate.utils.BitcoinUIModel; import java.util.ResourceBundle; /** * Gets created auto-magically by FXMLLoader via reflection. The widget fields are set to the GUI controls they're named * after. This class handles all the updates and event handling for the main UI. */ public class MainController { public HBox controlsBox; @FXML private ResourceBundle resources; @FXML private TabPane tabPane; @FXML private Font x1; @FXML private Button receiveMoneyBtn; @FXML private Button sendMoneyOutBtn; @FXML private ListView blockchainTxList; @FXML private Label balance; @FXML private Color x2; @FXML private Label balance1; @FXML private Color x22; @FXML private Font x11; @FXML private Button openChannel; @FXML private Button thunderReceiveMoneyBtn; @FXML private Button thunderSendMoneyOutBtn; @FXML private Button thunderRefreshBtn; @FXML private Button startListenButton; @FXML private Button fetchIPButton; @FXML private Button syncButton; @FXML private ListView nodesList; @FXML private ListView channelNetworkList; @FXML private ListView channelList; @FXML private ListView thunderTxListIncluded; @FXML private ListView thunderTxListSettled; @FXML private ListView thunderTxListOpen; @FXML private ListView thunderTxListRefunded; @FXML private Label thunderBalance; @FXML private Color x21; @FXML private Color x211; @FXML void fetchIPs (ActionEvent event) { Main.thunderContext.fetchNetworkIPs(new NullResultCommand()); } @FXML void startListen (ActionEvent event) { Main.thunderContext.startListening(new NullResultCommand()); } @FXML void syncThunder (ActionEvent event) { if (selectedNode != null) { Main.thunderContext.openChannel(selectedNode.pubkey, new NullResultCommand()); } } @FXML void onReloadButton (ActionEvent event) { Main.thunderContext.getSyncData(result -> { System.out.println("Sync finished: " + result.getMessage()); }); } @FXML void refresh (ActionEvent event) { model.update(); } private PubkeyIPObject selectedNode = null; private BitcoinUIModel model = new BitcoinUIModel(); private NotificationBarPane.Item syncItem; // Called by FXMLLoader. public void initialize () { } public void onBitcoinSetup () { model.init(); balance.textProperty().bind(EasyBind.map(model.balanceProperty(), Coin::toFriendlyString)); // Don't let the user click send money when the wallet is empty. sendMoneyOutBtn.disableProperty().bind(model.balanceProperty().isEqualTo(Coin.ZERO)); TorClient torClient = Main.bitcoin.peerGroup().getTorClient(); if (torClient != null) { SimpleDoubleProperty torProgress = new SimpleDoubleProperty(-1); String torMsg = "Initialising Tor"; syncItem = Main.instance.notificationBar.pushItem(torMsg, torProgress); torClient.addInitializationListener(new TorInitializationListener() { @Override public void initializationProgress (String message, int percent) { Platform.runLater(() -> { syncItem.label.set(torMsg + ": " + message); torProgress.set(percent / 100.0); }); } @Override public void initializationCompleted () { Platform.runLater(() -> { syncItem.cancel(); showBitcoinSyncMessage(); }); } }); } else { showBitcoinSyncMessage(); } model.syncProgressProperty().addListener(x -> { if (model.syncProgressProperty().get() >= 1.0) { readyToGoAnimation(); if (syncItem != null) { syncItem.cancel(); syncItem = null; } } else if (syncItem == null) { showBitcoinSyncMessage(); } }); Bindings.bindContent(blockchainTxList.getItems(), model.transactions); Bindings.bindContent(nodesList.getItems(), model.ipList); Bindings.bindContent(channelNetworkList.getItems(), model.channelNetworkList); Bindings.bindContent(channelList.getItems(), model.channelList); thunderBalance.textProperty().bind(EasyBind.map(model.balanceThunder, Coin::toFriendlyString)); nodesList.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<PubkeyIPObject>() { @Override public void changed (ObservableValue<? extends PubkeyIPObject> observable, PubkeyIPObject oldValue, PubkeyIPObject newValue) { System.out.println("ListView selection changed from oldValue = " + oldValue + " to newValue = " + newValue); selectedNode = newValue; } }); blockchainTxList.setCellFactory(param1 -> new TextFieldListCell(new StringConverter<Transaction>() { @Override public String toString (Transaction tx) { Coin value = tx.getValue(Main.bitcoin.wallet()); if (value.isPositive()) { return tx.getConfidence().getDepthInBlocks() + " Incoming payment of " + MonetaryFormat.BTC.format(value); } else if (value.isNegative()) { Address address = tx.getOutput(0).getAddressFromP2PKHScript(Main.params); if (address == null) { return tx.getConfidence().getDepthInBlocks() + " Outbound payment to ThunderChannel of " + value.toFriendlyString().substring(1); } return tx.getConfidence().getDepthInBlocks() + " Outbound payment to " + address; } return "Payment with id " + tx.getHash(); } @Override public Transaction fromString (String string) { return null; } })); blockchainTxList.setCellFactory(param -> new TextFieldListCell(new StringConverter<Transaction>() { @Override public String toString (Transaction tx) { Coin value = tx.getValue(Main.bitcoin.wallet()); if (value.isPositive()) { return tx.getConfidence().getDepthInBlocks() + " Incoming payment of " + MonetaryFormat.BTC.format(value); } else if (value.isNegative()) { Address address = tx.getOutput(0).getAddressFromP2PKHScript(Main.params); if (address == null) { return tx.getConfidence().getDepthInBlocks() + " Outbound payment to ThunderChannel of " + value.toFriendlyString().substring (1); } return tx.getConfidence().getDepthInBlocks() + " Outbound payment to " + address; } return "Payment with id " + tx.getHash(); } @Override public Transaction fromString (String string) { return null; } })); } private void showBitcoinSyncMessage () { } public void sendMoneyOut (ActionEvent event) { // Hide this UI and show the send money UI. This UI won't be clickable until the user dismisses send_money. Main.instance.overlayUI("send_money_blockchain.fxml"); } public void settingsClicked (ActionEvent event) { Main.OverlayUI<WalletSettingsController> screen = Main.instance.overlayUI("wallet_settings.fxml"); screen.controller.initialize(null); } public void restoreFromSeedAnimation () { // Buttons slide out ... TranslateTransition leave = new TranslateTransition(Duration.millis(1200), controlsBox); leave.setByY(80.0); leave.play(); } public void readyToGoAnimation () { // Buttons slide in and clickable address appears simultaneously. } public DownloadProgressTracker progressBarUpdater () { return model.getDownloadProgressTracker(); } public void receiveMoney (ActionEvent actionEvent) { Main.instance.overlayUI("receive_money_blockchain.fxml"); } @FXML void thunderReceiveMoney (ActionEvent event) { Main.instance.overlayUI("receive_money_request.fxml"); } @FXML void thunderSendMoneyOut (ActionEvent event) { Main.instance.overlayUI("send_money.fxml"); } @FXML void openChannel (ActionEvent event) { if (selectedNode != null) { Main.thunderContext.openChannel(selectedNode.pubkey, new NullResultCommand()); } } public void closeChannel (ActionEvent actionEvent) { //TODO } @FXML void thunderRefresh (ActionEvent event) { } }