wallettemplate.utils.TextFieldValidator Java Examples

The following examples show how to use wallettemplate.utils.TextFieldValidator. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Main.java    From thunder with GNU Affero General Public License v3.0 6 votes vote down vote up
private void prepareUI (Stage mainWindow) throws IOException {
    this.mainWindow = mainWindow;
    // Show the crash dialog for any exceptions that we don't handle and that hit the main loop.
    GuiUtils.handleCrashesOnThisThread();

    // Load the GUI. The MainController class will be automagically created and wired up.
    File file = new File("main.fxml");
    URL location = getClass().getResource("main.fxml");
    FXMLLoader loader = new FXMLLoader(location);
    mainUI = loader.load();
    controller = loader.getController();
    // Configure the window with a StackPane so we can overlay things on top of the main UI, and a
    // NotificationBarPane so we can slide messages and progress bars in from the bottom. Note that
    // ordering of the construction and connection matters here, otherwise we get (harmless) CSS error
    // spew to the logs.
    notificationBar = new NotificationBarPane(mainUI);
    mainWindow.setTitle(APP_NAME);
    uiStack = new StackPane();
    Scene scene = new Scene(uiStack);
    TextFieldValidator.configureScene(scene);   // Add CSS that we need.
    scene.getStylesheets().add(getClass().getResource("wallet.css").toString());
    uiStack.getChildren().add(notificationBar);
    mainWindow.setScene(scene);
}
 
Example #2
Source File: SendMoneyController.java    From green_android with GNU General Public License v3.0 5 votes vote down vote up
public void initialize() {
    Coin balance = Main.bitcoin.wallet().getBalance();
    checkState(!balance.isZero());
    new BitcoinAddressValidator(Main.params, address, sendBtn);
    new TextFieldValidator(amountEdit, text ->
            !WTUtils.didThrow(() -> checkState(Coin.parseCoin(text).compareTo(balance) <= 0)));
    amountEdit.setText(balance.toPlainString());
}
 
Example #3
Source File: BitcoinAddressValidator.java    From green_android with GNU General Public License v3.0 5 votes vote down vote up
public BitcoinAddressValidator(NetworkParameters params, TextField field, Node... nodes) {
    this.params = params;
    this.nodes = nodes;

    // Handle the red highlighting, but don't highlight in red just when the field is empty because that makes
    // the example/prompt address hard to read.
    new TextFieldValidator(field, text -> text.isEmpty() || testAddr(text));
    // However we do want the buttons to be disabled when empty so we apply a different test there.
    field.textProperty().addListener((observableValue, prev, current) -> {
        toggleButtons(current);
    });
    toggleButtons(field.getText());
}
 
Example #4
Source File: SendMoneyBlockchainController.java    From thunder with GNU Affero General Public License v3.0 5 votes vote down vote up
public void initialize () {
    Coin balance = Main.wallet.getBalance();
    checkState(!balance.isZero());
    new BitcoinAddressValidator(Constants.getNetwork(), address, sendBtn);
    new TextFieldValidator(amountEdit, text ->
            !WTUtils.didThrow(() -> checkState(Coin.parseCoin(text).compareTo(balance) <= 0)));
    amountEdit.setText(balance.toPlainString());
}
 
Example #5
Source File: BitcoinAddressValidator.java    From thunder with GNU Affero General Public License v3.0 5 votes vote down vote up
public BitcoinAddressValidator (NetworkParameters params, TextField field, Node... nodes) {
    this.params = params;
    this.nodes = nodes;

    // Handle the red highlighting, but don't highlight in red just when the field is empty because that makes
    // the example/prompt address hard to read.
    new TextFieldValidator(field, text -> text.isEmpty() || testAddr(text));
    // However we do want the buttons to be disabled when empty so we apply a different test there.
    field.textProperty().addListener((observableValue, prev, current) -> {
        toggleButtons(current);
    });
    toggleButtons(field.getText());
}
 
Example #6
Source File: SendMoneyController.java    From GreenBits with GNU General Public License v3.0 5 votes vote down vote up
public void initialize() {
    Coin balance = Main.bitcoin.wallet().getBalance();
    checkState(!balance.isZero());
    new BitcoinAddressValidator(Main.params, address, sendBtn);
    new TextFieldValidator(amountEdit, text ->
            !WTUtils.didThrow(() -> checkState(Coin.parseCoin(text).compareTo(balance) <= 0)));
    amountEdit.setText(balance.toPlainString());
}
 
Example #7
Source File: BitcoinAddressValidator.java    From GreenBits with GNU General Public License v3.0 5 votes vote down vote up
public BitcoinAddressValidator(NetworkParameters params, TextField field, Node... nodes) {
    this.params = params;
    this.nodes = nodes;

    // Handle the red highlighting, but don't highlight in red just when the field is empty because that makes
    // the example/prompt address hard to read.
    new TextFieldValidator(field, text -> text.isEmpty() || testAddr(text));
    // However we do want the buttons to be disabled when empty so we apply a different test there.
    field.textProperty().addListener((observableValue, prev, current) -> {
        toggleButtons(current);
    });
    toggleButtons(field.getText());
}
 
Example #8
Source File: SendMoneyBlockchainController.java    From thundernetwork with GNU Affero General Public License v3.0 5 votes vote down vote up
public void initialize() {
    Coin balance = Main.bitcoin.wallet().getBalance();
    checkState(!balance.isZero());
    new BitcoinAddressValidator(Main.params, address, sendBtn);
    new TextFieldValidator(amountEdit, text ->
            !WTUtils.didThrow(() -> checkState(Coin.parseCoin(text).compareTo(balance) <= 0)));
    amountEdit.setText(balance.toPlainString());
}
 
Example #9
Source File: BitcoinAddressValidator.java    From thundernetwork with GNU Affero General Public License v3.0 5 votes vote down vote up
public BitcoinAddressValidator(NetworkParameters params, TextField field, Node... nodes) {
    this.params = params;
    this.nodes = nodes;

    // Handle the red highlighting, but don't highlight in red just when the field is empty because that makes
    // the example/prompt address hard to read.
    new TextFieldValidator(field, text -> text.isEmpty() || testAddr(text));
    // However we do want the buttons to be disabled when empty so we apply a different test there.
    field.textProperty().addListener((observableValue, prev, current) -> {
        toggleButtons(current);
    });
    toggleButtons(field.getText());
}
 
Example #10
Source File: Main.java    From green_android with GNU General Public License v3.0 4 votes vote down vote up
private void realStart(Stage mainWindow) throws IOException {
    this.mainWindow = mainWindow;
    instance = this;
    // Show the crash dialog for any exceptions that we don't handle and that hit the main loop.
    GuiUtils.handleCrashesOnThisThread();

    if (System.getProperty("os.name").toLowerCase().contains("mac")) {
        // We could match the Mac Aqua style here, except that (a) Modena doesn't look that bad, and (b)
        // the date picker widget is kinda broken in AquaFx and I can't be bothered fixing it.
        // AquaFx.style();
    }

    // Load the GUI. The MainController class will be automagically created and wired up.
    URL location = getClass().getResource("main.fxml");
    FXMLLoader loader = new FXMLLoader(location);
    mainUI = loader.load();
    controller = loader.getController();
    // Configure the window with a StackPane so we can overlay things on top of the main UI, and a
    // NotificationBarPane so we can slide messages and progress bars in from the bottom. Note that
    // ordering of the construction and connection matters here, otherwise we get (harmless) CSS error
    // spew to the logs.
    notificationBar = new NotificationBarPane(mainUI);
    mainWindow.setTitle(APP_NAME);
    uiStack = new StackPane();
    Scene scene = new Scene(uiStack);
    TextFieldValidator.configureScene(scene);   // Add CSS that we need.
    scene.getStylesheets().add(getClass().getResource("wallet.css").toString());
    uiStack.getChildren().add(notificationBar);
    mainWindow.setScene(scene);

    // Make log output concise.
    BriefLogFormatter.init();
    // Tell bitcoinj to run 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
    // a future version.
    Threading.USER_THREAD = Platform::runLater;
    // Create the app kit. It won't do any heavyweight initialization until after we start it.
    setupWalletKit(null);

    if (bitcoin.isChainFileLocked()) {
        informationalAlert("Already running", "This application is already running and cannot be started twice.");
        Platform.exit();
        return;
    }

    mainWindow.show();

    WalletSetPasswordController.estimateKeyDerivationTimeMsec();

    bitcoin.addListener(new Service.Listener() {
        @Override
        public void failed(Service.State from, Throwable failure) {
            GuiUtils.crashAlert(failure);
        }
    }, Platform::runLater);
    bitcoin.startAsync();

    scene.getAccelerators().put(KeyCombination.valueOf("Shortcut+F"), () -> bitcoin.peerGroup().getDownloadPeer().close());
}
 
Example #11
Source File: WalletSettingsController.java    From green_android with GNU General Public License v3.0 4 votes vote down vote up
public void initialize(@Nullable KeyParameter aesKey) {
    DeterministicSeed seed = Main.bitcoin.wallet().getKeyChainSeed();
    if (aesKey == null) {
        if (seed.isEncrypted()) {
            log.info("Wallet is encrypted, requesting password first.");
            // Delay execution of this until after we've finished initialising this screen.
            Platform.runLater(() -> askForPasswordAndRetry());
            return;
        }
    } else {
        this.aesKey = aesKey;
        seed = seed.decrypt(checkNotNull(Main.bitcoin.wallet().getKeyCrypter()), "", aesKey);
        // Now we can display the wallet seed as appropriate.
        passwordButton.setText("Remove password");
    }

    // Set the date picker to show the birthday of this wallet.
    Instant creationTime = Instant.ofEpochSecond(seed.getCreationTimeSeconds());
    LocalDate origDate = creationTime.atZone(ZoneId.systemDefault()).toLocalDate();
    datePicker.setValue(origDate);

    // Set the mnemonic seed words.
    final List<String> mnemonicCode = seed.getMnemonicCode();
    checkNotNull(mnemonicCode);    // Already checked for encryption.
    String origWords = Utils.join(mnemonicCode);
    wordsArea.setText(origWords);

    // Validate words as they are being typed.
    MnemonicCode codec = unchecked(MnemonicCode::new);
    TextFieldValidator validator = new TextFieldValidator(wordsArea, text ->
        !didThrow(() -> codec.check(Splitter.on(' ').splitToList(text)))
    );

    // Clear the date picker if the user starts editing the words, if it contained the current wallets date.
    // This forces them to set the birthday field when restoring.
    wordsArea.textProperty().addListener(o -> {
        if (origDate.equals(datePicker.getValue()))
            datePicker.setValue(null);
    });

    BooleanBinding datePickerIsInvalid = or(
            datePicker.valueProperty().isNull(),

            createBooleanBinding(() ->
                    datePicker.getValue().isAfter(LocalDate.now())
            , /* depends on */ datePicker.valueProperty())
    );

    // Don't let the user click restore if the words area contains the current wallet words, or are an invalid set,
    // or if the date field isn't set, or if it's in the future.
    restoreButton.disableProperty().bind(
            or(
                    or(
                            not(validator.valid),
                            equal(origWords, wordsArea.textProperty())
                    ),

                    datePickerIsInvalid
            )
    );

    // Highlight the date picker in red if it's empty or in the future, so the user knows why restore is disabled.
    datePickerIsInvalid.addListener((dp, old, cur) -> {
        if (cur) {
            datePicker.getStyleClass().add("validation_error");
        } else {
            datePicker.getStyleClass().remove("validation_error");
        }
    });
}
 
Example #12
Source File: WalletSettingsController.java    From thunder with GNU Affero General Public License v3.0 4 votes vote down vote up
public void initialize (@Nullable KeyParameter aesKey) {
    DeterministicSeed seed = Main.wallet.getKeyChainSeed();
    if (aesKey == null) {
        if (seed.isEncrypted()) {
            log.info("Wallet is encrypted, requesting password first.");
            // Delay execution of this until after we've finished initialising this screen.
            Platform.runLater(() -> askForPasswordAndRetry());
            return;
        }
    } else {
        this.aesKey = aesKey;
        seed = seed.decrypt(checkNotNull(Main.wallet.getKeyCrypter()), "", aesKey);
        // Now we can display the wallet seed as appropriate.
        passwordButton.setText("Remove password");
    }

    // Set the date picker to show the birthday of this wallet.
    Instant creationTime = Instant.ofEpochSecond(seed.getCreationTimeSeconds());
    LocalDate origDate = creationTime.atZone(ZoneId.systemDefault()).toLocalDate();
    datePicker.setValue(origDate);

    // Set the mnemonic seed words.
    final List<String> mnemonicCode = seed.getMnemonicCode();
    checkNotNull(mnemonicCode);    // Already checked for encryption.
    String origWords = Utils.join(mnemonicCode);
    wordsArea.setText(origWords);

    // Validate words as they are being typed.
    MnemonicCode codec = unchecked(MnemonicCode::new);
    TextFieldValidator validator = new TextFieldValidator(wordsArea, text ->
            !didThrow(() -> codec.check(Splitter.on(' ').splitToList(text)))
    );

    // Clear the date picker if the user starts editing the words, if it contained the current wallets date.
    // This forces them to set the birthday field when restoring.
    wordsArea.textProperty().addListener(o -> {
        if (origDate.equals(datePicker.getValue())) {
            datePicker.setValue(null);
        }
    });

    BooleanBinding datePickerIsInvalid = or(
            datePicker.valueProperty().isNull(),

            createBooleanBinding(() ->
                            datePicker.getValue().isAfter(LocalDate.now())
                    , /* depends on */ datePicker.valueProperty())
    );

    // Don't let the user click restore if the words area contains the current wallet words, or are an invalid set,
    // or if the date field isn't set, or if it's in the future.
    restoreButton.disableProperty().bind(
            or(
                    or(
                            not(validator.valid),
                            equal(origWords, wordsArea.textProperty())
                    ),

                    datePickerIsInvalid
            )
    );

    // Highlight the date picker in red if it's empty or in the future, so the user knows why restore is disabled.
    datePickerIsInvalid.addListener((dp, old, cur) -> {
        if (cur) {
            datePicker.getStyleClass().add("validation_error");
        } else {
            datePicker.getStyleClass().remove("validation_error");
        }
    });
}
 
Example #13
Source File: Main.java    From GreenBits with GNU General Public License v3.0 4 votes vote down vote up
private void realStart(Stage mainWindow) throws IOException {
    this.mainWindow = mainWindow;
    instance = this;
    // Show the crash dialog for any exceptions that we don't handle and that hit the main loop.
    GuiUtils.handleCrashesOnThisThread();

    if (System.getProperty("os.name").toLowerCase().contains("mac")) {
        // We could match the Mac Aqua style here, except that (a) Modena doesn't look that bad, and (b)
        // the date picker widget is kinda broken in AquaFx and I can't be bothered fixing it.
        // AquaFx.style();
    }

    // Load the GUI. The MainController class will be automagically created and wired up.
    URL location = getClass().getResource("main.fxml");
    FXMLLoader loader = new FXMLLoader(location);
    mainUI = loader.load();
    controller = loader.getController();
    // Configure the window with a StackPane so we can overlay things on top of the main UI, and a
    // NotificationBarPane so we can slide messages and progress bars in from the bottom. Note that
    // ordering of the construction and connection matters here, otherwise we get (harmless) CSS error
    // spew to the logs.
    notificationBar = new NotificationBarPane(mainUI);
    mainWindow.setTitle(APP_NAME);
    uiStack = new StackPane();
    Scene scene = new Scene(uiStack);
    TextFieldValidator.configureScene(scene);   // Add CSS that we need.
    scene.getStylesheets().add(getClass().getResource("wallet.css").toString());
    uiStack.getChildren().add(notificationBar);
    mainWindow.setScene(scene);

    // Make log output concise.
    BriefLogFormatter.init();
    // Tell bitcoinj to run 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
    // a future version.
    Threading.USER_THREAD = Platform::runLater;
    // Create the app kit. It won't do any heavyweight initialization until after we start it.
    setupWalletKit(null);

    if (bitcoin.isChainFileLocked()) {
        informationalAlert("Already running", "This application is already running and cannot be started twice.");
        Platform.exit();
        return;
    }

    mainWindow.show();

    WalletSetPasswordController.estimateKeyDerivationTimeMsec();

    bitcoin.addListener(new Service.Listener() {
        @Override
        public void failed(Service.State from, Throwable failure) {
            GuiUtils.crashAlert(failure);
        }
    }, Platform::runLater);
    bitcoin.startAsync();

    scene.getAccelerators().put(KeyCombination.valueOf("Shortcut+F"), () -> bitcoin.peerGroup().getDownloadPeer().close());
}
 
Example #14
Source File: WalletSettingsController.java    From GreenBits with GNU General Public License v3.0 4 votes vote down vote up
public void initialize(@Nullable KeyParameter aesKey) {
    DeterministicSeed seed = Main.bitcoin.wallet().getKeyChainSeed();
    if (aesKey == null) {
        if (seed.isEncrypted()) {
            log.info("Wallet is encrypted, requesting password first.");
            // Delay execution of this until after we've finished initialising this screen.
            Platform.runLater(() -> askForPasswordAndRetry());
            return;
        }
    } else {
        this.aesKey = aesKey;
        seed = seed.decrypt(checkNotNull(Main.bitcoin.wallet().getKeyCrypter()), "", aesKey);
        // Now we can display the wallet seed as appropriate.
        passwordButton.setText("Remove password");
    }

    // Set the date picker to show the birthday of this wallet.
    Instant creationTime = Instant.ofEpochSecond(seed.getCreationTimeSeconds());
    LocalDate origDate = creationTime.atZone(ZoneId.systemDefault()).toLocalDate();
    datePicker.setValue(origDate);

    // Set the mnemonic seed words.
    final List<String> mnemonicCode = seed.getMnemonicCode();
    checkNotNull(mnemonicCode);    // Already checked for encryption.
    String origWords = Utils.join(mnemonicCode);
    wordsArea.setText(origWords);

    // Validate words as they are being typed.
    MnemonicCode codec = unchecked(MnemonicCode::new);
    TextFieldValidator validator = new TextFieldValidator(wordsArea, text ->
        !didThrow(() -> codec.check(Splitter.on(' ').splitToList(text)))
    );

    // Clear the date picker if the user starts editing the words, if it contained the current wallets date.
    // This forces them to set the birthday field when restoring.
    wordsArea.textProperty().addListener(o -> {
        if (origDate.equals(datePicker.getValue()))
            datePicker.setValue(null);
    });

    BooleanBinding datePickerIsInvalid = or(
            datePicker.valueProperty().isNull(),

            createBooleanBinding(() ->
                    datePicker.getValue().isAfter(LocalDate.now())
            , /* depends on */ datePicker.valueProperty())
    );

    // Don't let the user click restore if the words area contains the current wallet words, or are an invalid set,
    // or if the date field isn't set, or if it's in the future.
    restoreButton.disableProperty().bind(
            or(
                    or(
                            not(validator.valid),
                            equal(origWords, wordsArea.textProperty())
                    ),

                    datePickerIsInvalid
            )
    );

    // Highlight the date picker in red if it's empty or in the future, so the user knows why restore is disabled.
    datePickerIsInvalid.addListener((dp, old, cur) -> {
        if (cur) {
            datePicker.getStyleClass().add("validation_error");
        } else {
            datePicker.getStyleClass().remove("validation_error");
        }
    });
}
 
Example #15
Source File: Main.java    From thundernetwork with GNU Affero General Public License v3.0 4 votes vote down vote up
private void realStart (Stage mainWindow) throws IOException {
    this.mainWindow = mainWindow;
    instance = this;
    // Show the crash dialog for any exceptions that we don't handle and that hit the main loop.
    GuiUtils.handleCrashesOnThisThread();

    // Load the GUI. The MainController class will be automagically created and wired up.
    File file = new File("main.fxml");
    URL location = getClass().getResource("main.fxml");
    FXMLLoader loader = new FXMLLoader(location);
    mainUI = loader.load();
    controller = loader.getController();
    // Configure the window with a StackPane so we can overlay things on top of the main UI, and a
    // NotificationBarPane so we can slide messages and progress bars in from the bottom. Note that
    // ordering of the construction and connection matters here, otherwise we get (harmless) CSS error
    // spew to the logs.
    notificationBar = new NotificationBarPane(mainUI);
    mainWindow.setTitle(APP_NAME);
    uiStack = new StackPane();
    Scene scene = new Scene(uiStack);
    TextFieldValidator.configureScene(scene);   // Add CSS that we need.
    scene.getStylesheets().add(getClass().getResource("wallet.css").toString());
    uiStack.getChildren().add(notificationBar);
    mainWindow.setScene(scene);

    // Make log output concise.
    BriefLogFormatter.init();
    // Tell bitcoinj to run 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
    // a future version.
    Threading.USER_THREAD = Platform::runLater;
    // Create the app kit. It won't do any heavyweight initialization until after we start it.
    setupWalletKit(null);

    if (bitcoin.isChainFileLocked()) {
        if (REQUEST != null) {
            PaymentProtocolClientSocket.sendPaymentRequest(REQUEST);
            Platform.exit();
            return;
        }
        informationalAlert("Already running", "This application is already running and cannot be started twice.");
        Platform.exit();
        return;
    }
    PaymentProtocolServerSocket.init();

    mainWindow.show();

    WalletSetPasswordController.estimateKeyDerivationTimeMsec();

    bitcoin.addListener(new Service.Listener() {
        @Override
        public void failed (Service.State from, Throwable failure) {
            GuiUtils.crashAlert(failure);
        }
    }, Platform::runLater);
    bitcoin.startAsync();

    System.out.println("init");
    node.init();
    wallet = new MockWallet(Constants.getNetwork());
    thunderContext = new ThunderContext(wallet, dbHandler, node);
    thunderContext.startUp(new NullResultCommand());

    scene.getAccelerators().put(KeyCombination.valueOf("Shortcut+F"), () -> bitcoin.peerGroup().getDownloadPeer().close());
}
 
Example #16
Source File: WalletSettingsController.java    From thundernetwork with GNU Affero General Public License v3.0 4 votes vote down vote up
public void initialize(@Nullable KeyParameter aesKey) {
    DeterministicSeed seed = Main.bitcoin.wallet().getKeyChainSeed();
    if (aesKey == null) {
        if (seed.isEncrypted()) {
            log.info("Wallet is encrypted, requesting password first.");
            // Delay execution of this until after we've finished initialising this screen.
            Platform.runLater(() -> askForPasswordAndRetry());
            return;
        }
    } else {
        this.aesKey = aesKey;
        seed = seed.decrypt(checkNotNull(Main.bitcoin.wallet().getKeyCrypter()), "", aesKey);
        // Now we can display the wallet seed as appropriate.
        passwordButton.setText("Remove password");
    }

    // Set the date picker to show the birthday of this wallet.
    Instant creationTime = Instant.ofEpochSecond(seed.getCreationTimeSeconds());
    LocalDate origDate = creationTime.atZone(ZoneId.systemDefault()).toLocalDate();
    datePicker.setValue(origDate);

    // Set the mnemonic seed words.
    final List<String> mnemonicCode = seed.getMnemonicCode();
    checkNotNull(mnemonicCode);    // Already checked for encryption.
    String origWords = Utils.join(mnemonicCode);
    wordsArea.setText(origWords);

    // Validate words as they are being typed.
    MnemonicCode codec = unchecked(MnemonicCode::new);
    TextFieldValidator validator = new TextFieldValidator(wordsArea, text ->
        !didThrow(() -> codec.check(Splitter.on(' ').splitToList(text)))
    );

    // Clear the date picker if the user starts editing the words, if it contained the current wallets date.
    // This forces them to set the birthday field when restoring.
    wordsArea.textProperty().addListener(o -> {
        if (origDate.equals(datePicker.getValue()))
            datePicker.setValue(null);
    });

    BooleanBinding datePickerIsInvalid = or(
            datePicker.valueProperty().isNull(),

            createBooleanBinding(() ->
                    datePicker.getValue().isAfter(LocalDate.now())
            , /* depends on */ datePicker.valueProperty())
    );

    // Don't let the user click restore if the words area contains the current wallet words, or are an invalid set,
    // or if the date field isn't set, or if it's in the future.
    restoreButton.disableProperty().bind(
            or(
                    or(
                            not(validator.valid),
                            equal(origWords, wordsArea.textProperty())
                    ),

                    datePickerIsInvalid
            )
    );

    // Highlight the date picker in red if it's empty or in the future, so the user knows why restore is disabled.
    datePickerIsInvalid.addListener((dp, old, cur) -> {
        if (cur) {
            datePicker.getStyleClass().add("validation_error");
        } else {
            datePicker.getStyleClass().remove("validation_error");
        }
    });
}