package io.bitsquare.gui.main.overlays.editor; import io.bitsquare.alert.PrivateNotificationManager; import io.bitsquare.gui.components.InputTextField; import io.bitsquare.gui.main.overlays.Overlay; import io.bitsquare.gui.main.overlays.windows.SendPrivateNotificationWindow; import io.bitsquare.gui.util.FormBuilder; import io.bitsquare.trade.offer.Offer; import io.bitsquare.user.Preferences; import javafx.animation.Interpolator; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; import javafx.animation.Timeline; import javafx.beans.value.ChangeListener; import javafx.collections.ObservableList; import javafx.event.EventHandler; import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.geometry.Point2D; import javafx.scene.Camera; import javafx.scene.PerspectiveCamera; import javafx.scene.Scene; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyEvent; import javafx.scene.layout.GridPane; import javafx.scene.transform.Rotate; import javafx.stage.Modality; import javafx.stage.Window; import javafx.util.Duration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Map; import java.util.function.Consumer; public class PeerInfoWithTagEditor extends Overlay<PeerInfoWithTagEditor> { private static final Logger log = LoggerFactory.getLogger(PeerInfoWithTagEditor.class); private InputTextField inputTextField; private Point2D position; private static PeerInfoWithTagEditor INSTANCE; private Consumer<String> saveHandler; private String hostName; private int numTrades; private ChangeListener<Boolean> focusListener; private PrivateNotificationManager privateNotificationManager; private Offer offer; private EventHandler<KeyEvent> keyEventEventHandler; public PeerInfoWithTagEditor(PrivateNotificationManager privateNotificationManager, Offer offer) { this.privateNotificationManager = privateNotificationManager; this.offer = offer; width = 400; type = Type.Undefined; if (INSTANCE != null) INSTANCE.hide(); INSTANCE = this; } public PeerInfoWithTagEditor onSave(Consumer<String> saveHandler) { this.saveHandler = saveHandler; return this; } public PeerInfoWithTagEditor position(Point2D position) { this.position = position; return this; } public PeerInfoWithTagEditor hostName(String hostName) { this.hostName = hostName; return this; } public PeerInfoWithTagEditor numTrades(int numTrades) { this.numTrades = numTrades; return this; } @Override public void show() { headLine("Peer info"); actionButtonText("Save"); createGridPane(); addHeadLine(); addContent(); addCloseButton(); applyStyles(); onShow(); } @Override protected void onShow() { super.display(); if (stage != null) { focusListener = (observable, oldValue, newValue) -> { if (!newValue) hide(); }; stage.focusedProperty().addListener(focusListener); Scene scene = stage.getScene(); if (scene != null) scene.addEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler); } } @Override public void hide() { animateHide(() -> { removeEffectFromBackground(); if (stage != null) stage.hide(); else log.warn("Stage is null"); cleanup(); onHidden(); }); } @Override protected void onHidden() { INSTANCE = null; if (stage != null) { if (focusListener != null) stage.focusedProperty().removeListener(focusListener); Scene scene = stage.getScene(); if (scene != null) scene.removeEventHandler(KeyEvent.KEY_RELEASED, keyEventEventHandler); } } protected void addContent() { FormBuilder.addLabelTextField(gridPane, ++rowIndex, "Onion address:", hostName).second.setMouseTransparent(false); FormBuilder.addLabelTextField(gridPane, ++rowIndex, "Number of completed trades:", String.valueOf(numTrades)); inputTextField = FormBuilder.addLabelInputTextField(gridPane, ++rowIndex, "Set tag for that peer:").second; Map<String, String> peerTagMap = Preferences.INSTANCE.getPeerTagMap(); String tag = peerTagMap.containsKey(hostName) ? peerTagMap.get(hostName) : ""; inputTextField.setText(tag); keyEventEventHandler = event -> { if (new KeyCodeCombination(KeyCode.R, KeyCombination.ALT_DOWN).match(event)) { new SendPrivateNotificationWindow(offer.getPubKeyRing(), offer.getOffererNodeAddress()) .onAddAlertMessage(privateNotificationManager::sendPrivateNotificationMessageIfKeyIsValid) .show(); } }; } @Override protected void addHeadLine() { super.addHeadLine(); GridPane.setHalignment(headLineLabel, HPos.CENTER); } protected void setupKeyHandler(Scene scene) { scene.setOnKeyPressed(e -> { if (e.getCode() == KeyCode.ESCAPE) { e.consume(); doClose(); } if (e.getCode() == KeyCode.ENTER) { e.consume(); save(); } }); } @Override protected void animateHide(Runnable onFinishedHandler) { if (Preferences.INSTANCE.getUseAnimations()) { double duration = getDuration(300); Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1); gridPane.setRotationAxis(Rotate.X_AXIS); Camera camera = gridPane.getScene().getCamera(); gridPane.getScene().setCamera(new PerspectiveCamera()); Timeline timeline = new Timeline(); ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames(); keyFrames.add(new KeyFrame(Duration.millis(0), new KeyValue(gridPane.rotateProperty(), 0, interpolator), new KeyValue(gridPane.opacityProperty(), 1, interpolator) )); keyFrames.add(new KeyFrame(Duration.millis(duration), new KeyValue(gridPane.rotateProperty(), -90, interpolator), new KeyValue(gridPane.opacityProperty(), 0, interpolator) )); timeline.setOnFinished(event -> { gridPane.setRotate(0); gridPane.setRotationAxis(Rotate.Z_AXIS); gridPane.getScene().setCamera(camera); onFinishedHandler.run(); }); timeline.play(); } else { onFinishedHandler.run(); } } @Override protected void animateDisplay() { if (Preferences.INSTANCE.getUseAnimations()) { double startY = -160; double duration = getDuration(400); Interpolator interpolator = Interpolator.SPLINE(0.25, 0.1, 0.25, 1); double height = gridPane.getPrefHeight(); Timeline timeline = new Timeline(); ObservableList<KeyFrame> keyFrames = timeline.getKeyFrames(); keyFrames.add(new KeyFrame(Duration.millis(0), new KeyValue(gridPane.opacityProperty(), 0, interpolator), new KeyValue(gridPane.translateYProperty(), startY, interpolator) )); keyFrames.add(new KeyFrame(Duration.millis(duration), new KeyValue(gridPane.opacityProperty(), 1, interpolator), new KeyValue(gridPane.translateYProperty(), 0, interpolator) )); timeline.play(); } } @Override protected void createGridPane() { super.createGridPane(); gridPane.setPadding(new Insets(15, 15, 30, 30)); } @Override protected void addCloseButton() { buttonDistance = 10; super.addCloseButton(); actionButton.setOnAction(event -> save()); } private void save() { hide(); if (saveHandler != null && inputTextField != null) saveHandler.accept(inputTextField.getText()); } @Override protected void applyStyles() { gridPane.setId("peer-info-popup-bg"); if (headLineLabel != null) headLineLabel.setId("peer-info-popup-headline"); } @Override protected void setModality() { stage.initOwner(owner.getScene().getWindow()); stage.initModality(Modality.NONE); } @Override protected void layout() { Window window = owner.getScene().getWindow(); stage.setX(Math.round(window.getX() + position.getX() - width)); stage.setY(Math.round(window.getY() + position.getY())); } @Override protected void addEffectToBackground() { } @Override protected void removeEffectFromBackground() { } }