priceStringConverter) {
+ this.seriesStyleClass = seriesStyleClass;
+ this.dataStyleClass = dataStyleClass;
+
+ setAutoSizeChildren(false);
+ getChildren().addAll(highLowLine, bar);
+ getStyleClass().setAll("candlestick-candle", seriesStyleClass, dataStyleClass);
+ updateStyleClasses();
+
+ tooltipContent = new TooltipContent(priceStringConverter);
+ tooltip.setGraphic(tooltipContent);
+ Tooltip.install(this, tooltip);
+ }
+
+ public void setSeriesAndDataStyleClasses(String seriesStyleClass, String dataStyleClass) {
+ this.seriesStyleClass = seriesStyleClass;
+ this.dataStyleClass = dataStyleClass;
+ getStyleClass().setAll("candlestick-candle", seriesStyleClass, dataStyleClass);
+ updateStyleClasses();
+ }
+
+ public void update(double closeOffset, double highOffset, double lowOffset, double candleWidth) {
+ this.closeOffset = closeOffset;
+ openAboveClose = closeOffset > 0;
+ updateStyleClasses();
+ highLowLine.setStartY(highOffset);
+ highLowLine.setEndY(lowOffset);
+ if (openAboveClose) {
+ bar.resizeRelocate(-candleWidth / 2, 0, candleWidth, Math.max(5, closeOffset));
+ } else {
+ bar.resizeRelocate(-candleWidth / 2, closeOffset, candleWidth, Math.max(5, closeOffset * -1));
+ }
+ }
+
+ public void updateTooltip(CandleData candleData) {
+ tooltipContent.update(candleData);
+ }
+
+ private void updateStyleClasses() {
+ String style = openAboveClose ? "open-above-close" : "close-above-open";
+ if (closeOffset == 0)
+ style = "empty";
+
+ highLowLine.getStyleClass().setAll("candlestick-line", seriesStyleClass, dataStyleClass,
+ style);
+
+ bar.getStyleClass().setAll("candlestick-bar", seriesStyleClass, dataStyleClass,
+ style);
+ }
+}
\ No newline at end of file
diff --git a/gui/src/main/java/io/bitsquare/gui/main/markets/trades/charts/price/CandleStickChart.java b/gui/src/main/java/io/bitsquare/gui/main/markets/trades/charts/price/CandleStickChart.java
new file mode 100644
index 0000000000..ba8c2b1298
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/gui/main/markets/trades/charts/price/CandleStickChart.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 2008, 2014, Oracle and/or its affiliates.
+ * All rights reserved. Use is subject to license terms.
+ *
+ * This file is available and licensed under the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * - Neither the name of Oracle Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package io.bitsquare.gui.main.markets.trades.charts.price;
+
+import io.bitsquare.gui.main.markets.trades.charts.CandleData;
+import javafx.animation.FadeTransition;
+import javafx.event.ActionEvent;
+import javafx.scene.Node;
+import javafx.scene.chart.Axis;
+import javafx.scene.chart.NumberAxis;
+import javafx.scene.chart.XYChart;
+import javafx.scene.shape.LineTo;
+import javafx.scene.shape.MoveTo;
+import javafx.scene.shape.Path;
+import javafx.util.Duration;
+import javafx.util.StringConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A candlestick chart is a style of bar-chart used primarily to describe price movements of a security, derivative,
+ * or currency over time.
+ *
+ * The Data Y value is used for the opening price and then the close, high and low values are stored in the Data's
+ * extra value property using a CandleStickExtraValues object.
+ */
+public class CandleStickChart extends XYChart {
+ private static final Logger log = LoggerFactory.getLogger(CandleStickChart.class);
+
+ private StringConverter priceStringConverter;
+
+ // -------------- CONSTRUCTORS ----------------------------------------------
+
+ /**
+ * Construct a new CandleStickChart with the given axis.
+ *
+ * @param xAxis The x axis to use
+ * @param yAxis The y axis to use
+ */
+ public CandleStickChart(Axis xAxis, Axis yAxis) {
+ super(xAxis, yAxis);
+ }
+
+ // -------------- METHODS ------------------------------------------------------------------------------------------
+
+ public final void setToolTipStringConverter(StringConverter priceStringConverter) {
+ this.priceStringConverter = priceStringConverter;
+ }
+
+ /**
+ * Called to update and layout the content for the plot
+ */
+ @Override
+ protected void layoutPlotChildren() {
+ // we have nothing to layout if no data is present
+ if (getData() == null) {
+ return;
+ }
+ // update candle positions
+ for (int seriesIndex = 0; seriesIndex < getData().size(); seriesIndex++) {
+ XYChart.Series series = getData().get(seriesIndex);
+ Iterator> iter = getDisplayedDataIterator(series);
+ Path seriesPath = null;
+ if (series.getNode() instanceof Path) {
+ seriesPath = (Path) series.getNode();
+ seriesPath.getElements().clear();
+ }
+ while (iter.hasNext()) {
+ XYChart.Data item = iter.next();
+ double x = getXAxis().getDisplayPosition(getCurrentDisplayedXValue(item));
+ double y = getYAxis().getDisplayPosition(getCurrentDisplayedYValue(item));
+ Node itemNode = item.getNode();
+ CandleData candleData = (CandleData) item.getExtraValue();
+ if (itemNode instanceof Candle && candleData != null) {
+ Candle candle = (Candle) itemNode;
+
+ double close = getYAxis().getDisplayPosition(candleData.close);
+ double high = getYAxis().getDisplayPosition(candleData.high);
+ double low = getYAxis().getDisplayPosition(candleData.low);
+ // calculate candle width
+ double candleWidth = -1;
+ if (getXAxis() instanceof NumberAxis) {
+ NumberAxis xa = (NumberAxis) getXAxis();
+ candleWidth = xa.getDisplayPosition(xa.getTickUnit()) * 0.90; // use 90% width between ticks
+ }
+ // update candle
+ candle.update(close - y, high - y, low - y, candleWidth);
+ candle.updateTooltip(candleData);
+
+ // position the candle
+ candle.setLayoutX(x);
+ candle.setLayoutY(y);
+ }
+ if (seriesPath != null && candleData != null) {
+ final double displayPosition = getYAxis().getDisplayPosition(candleData.average);
+ if (seriesPath.getElements().isEmpty())
+ seriesPath.getElements().add(new MoveTo(x, displayPosition));
+ else
+ seriesPath.getElements().add(new LineTo(x, displayPosition));
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void dataItemChanged(XYChart.Data item) {
+ }
+
+ @Override
+ protected void dataItemAdded(XYChart.Series series, int itemIndex, XYChart.Data item) {
+ Node candle = createCandle(getData().indexOf(series), item, itemIndex);
+ if (getPlotChildren().contains(candle))
+ getPlotChildren().remove(candle);
+
+ if (shouldAnimate()) {
+ candle.setOpacity(0);
+ getPlotChildren().add(candle);
+ // fade in new candle
+ FadeTransition ft = new FadeTransition(Duration.millis(500), candle);
+ ft.setToValue(1);
+ ft.play();
+ } else {
+ getPlotChildren().add(candle);
+ }
+ // always draw average line on top
+
+ if (series.getNode() instanceof Path) {
+ Path seriesPath = (Path) series.getNode();
+ seriesPath.toFront();
+ }
+ }
+
+ @Override
+ protected void dataItemRemoved(XYChart.Data item, XYChart.Series series) {
+ if (series.getNode() instanceof Path) {
+ Path seriesPath = (Path) series.getNode();
+ seriesPath.getElements().clear();
+ }
+
+ final Node node = item.getNode();
+ if (shouldAnimate()) {
+ // fade out old candle
+ FadeTransition ft = new FadeTransition(Duration.millis(500), node);
+ ft.setToValue(0);
+ ft.setOnFinished((ActionEvent actionEvent) -> {
+ getPlotChildren().remove(node);
+ });
+ ft.play();
+ } else {
+ getPlotChildren().remove(node);
+ }
+ }
+
+ @Override
+ protected void seriesAdded(XYChart.Series series, int seriesIndex) {
+ // handle any data already in series
+ for (int j = 0; j < series.getData().size(); j++) {
+ XYChart.Data item = series.getData().get(j);
+ Node candle = createCandle(seriesIndex, item, j);
+
+ if (!getPlotChildren().contains(candle)) {
+ getPlotChildren().add(candle);
+ if (shouldAnimate()) {
+ candle.setOpacity(0);
+ FadeTransition ft = new FadeTransition(Duration.millis(500), candle);
+ ft.setToValue(1);
+ ft.play();
+ }
+ }
+ }
+ Path seriesPath = new Path();
+ seriesPath.getStyleClass().setAll("candlestick-average-line", "series" + seriesIndex);
+ series.setNode(seriesPath);
+
+ if (!getPlotChildren().contains(seriesPath)) {
+ getPlotChildren().add(seriesPath);
+ if (shouldAnimate()) {
+ seriesPath.setOpacity(0);
+ FadeTransition ft = new FadeTransition(Duration.millis(500), seriesPath);
+ ft.setToValue(1);
+ ft.play();
+ }
+ }
+ }
+
+ @Override
+ protected void seriesRemoved(XYChart.Series series) {
+ // remove all candle nodes
+ for (XYChart.Data d : series.getData()) {
+ final Node candle = d.getNode();
+ if (shouldAnimate()) {
+ FadeTransition ft = new FadeTransition(Duration.millis(500), candle);
+ ft.setToValue(0);
+ ft.setOnFinished((ActionEvent actionEvent) -> {
+ getPlotChildren().remove(candle);
+ });
+ ft.play();
+ } else {
+ getPlotChildren().remove(candle);
+ }
+ }
+ if (series.getNode() instanceof Path) {
+ Path seriesPath = (Path) series.getNode();
+ if (shouldAnimate()) {
+ FadeTransition ft = new FadeTransition(Duration.millis(500), seriesPath);
+ ft.setToValue(0);
+ ft.setOnFinished((ActionEvent actionEvent) -> {
+ getPlotChildren().remove(seriesPath);
+ seriesPath.getElements().clear();
+ });
+ ft.play();
+ } else {
+ getPlotChildren().remove(seriesPath);
+ seriesPath.getElements().clear();
+ }
+ }
+ }
+
+ /**
+ * Create a new Candle node to represent a single data item
+ *
+ * @param seriesIndex The index of the series the data item is in
+ * @param item The data item to create node for
+ * @param itemIndex The index of the data item in the series
+ * @return New candle node to represent the give data item
+ */
+ private Node createCandle(int seriesIndex, final XYChart.Data item, int itemIndex) {
+ Node candle = item.getNode();
+ // check if candle has already been created
+ if (candle instanceof Candle) {
+ ((Candle) candle).setSeriesAndDataStyleClasses("series" + seriesIndex, "data" + itemIndex);
+ } else {
+ candle = new Candle("series" + seriesIndex, "data" + itemIndex, priceStringConverter);
+ item.setNode(candle);
+ }
+ return candle;
+ }
+
+ /**
+ * This is called when the range has been invalidated and we need to update it. If the axis are auto
+ * ranging then we compile a list of all data that the given axis has to plot and call invalidateRange() on the
+ * axis passing it that data.
+ */
+ @Override
+ protected void updateAxisRange() {
+ // For candle stick chart we need to override this method as we need to let the axis know that they need to be able
+ // to cover the whole area occupied by the high to low range not just its center data value
+ final Axis xa = getXAxis();
+ final Axis ya = getYAxis();
+ List xData = null;
+ List yData = null;
+ if (xa.isAutoRanging()) {
+ xData = new ArrayList<>();
+ }
+ if (ya.isAutoRanging()) {
+ yData = new ArrayList<>();
+ }
+ if (xData != null || yData != null) {
+ for (XYChart.Series series : getData()) {
+ for (XYChart.Data data : series.getData()) {
+ if (xData != null) {
+ xData.add(data.getXValue());
+ }
+ if (yData != null) {
+ if (data.getExtraValue() instanceof CandleData) {
+ CandleData candleData = (CandleData) data.getExtraValue();
+ yData.add(candleData.high);
+ yData.add(candleData.low);
+ } else {
+ yData.add(data.getYValue());
+ }
+ }
+ }
+ }
+ if (xData != null) {
+ xa.invalidateRange(xData);
+ }
+ if (yData != null) {
+ ya.invalidateRange(yData);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/gui/src/main/java/io/bitsquare/gui/main/markets/trades/charts/price/TooltipContent.java b/gui/src/main/java/io/bitsquare/gui/main/markets/trades/charts/price/TooltipContent.java
new file mode 100644
index 0000000000..31a736977c
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/gui/main/markets/trades/charts/price/TooltipContent.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2008, 2014, Oracle and/or its affiliates.
+ * All rights reserved. Use is subject to license terms.
+ *
+ * This file is available and licensed under the following license:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution.
+ * - Neither the name of Oracle Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package io.bitsquare.gui.main.markets.trades.charts.price;
+
+import io.bitsquare.gui.main.markets.trades.charts.CandleData;
+import io.bitsquare.gui.util.Layout;
+import javafx.scene.control.Label;
+import javafx.scene.layout.GridPane;
+import javafx.util.StringConverter;
+
+/**
+ * The content for Candle tool tips
+ */
+public class TooltipContent extends GridPane {
+ private final StringConverter priceStringConverter;
+ private final Label openValue = new Label();
+ private final Label closeValue = new Label();
+ private final Label highValue = new Label();
+ private final Label lowValue = new Label();
+ private final Label averageValue = new Label();
+
+ TooltipContent(StringConverter priceStringConverter) {
+ this.priceStringConverter = priceStringConverter;
+
+ setHgap(Layout.GRID_GAP);
+ setVgap(2);
+
+ Label open = new Label("Open:");
+ Label close = new Label("Close:");
+ Label high = new Label("High:");
+ Label low = new Label("Low:");
+ Label average = new Label("Average:");
+ /* open.getStyleClass().add("candlestick-tooltip-label");
+ close.getStyleClass().add("candlestick-tooltip-label");
+ high.getStyleClass().add("candlestick-tooltip-label");
+ low.getStyleClass().add("candlestick-tooltip-label");*/
+ setConstraints(open, 0, 0);
+ setConstraints(openValue, 1, 0);
+ setConstraints(close, 0, 1);
+ setConstraints(closeValue, 1, 1);
+ setConstraints(high, 0, 2);
+ setConstraints(highValue, 1, 2);
+ setConstraints(low, 0, 3);
+ setConstraints(lowValue, 1, 3);
+ setConstraints(average, 0, 4);
+ setConstraints(averageValue, 1, 4);
+ getChildren().addAll(open, openValue, close, closeValue, high, highValue, low, lowValue, average, averageValue);
+ }
+
+ public void update(CandleData candleData) {
+ openValue.setText(priceStringConverter.toString(candleData.open));
+ closeValue.setText(priceStringConverter.toString(candleData.close));
+ highValue.setText(priceStringConverter.toString(candleData.high));
+ lowValue.setText(priceStringConverter.toString(candleData.low));
+ averageValue.setText(priceStringConverter.toString(candleData.average));
+ }
+}
\ No newline at end of file
diff --git a/gui/src/main/java/io/bitsquare/gui/main/markets/trades/charts/volume/VolumeBar.java b/gui/src/main/java/io/bitsquare/gui/main/markets/trades/charts/volume/VolumeBar.java
new file mode 100644
index 0000000000..1bad815254
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/gui/main/markets/trades/charts/volume/VolumeBar.java
@@ -0,0 +1,62 @@
+/*
+ * 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 io.bitsquare.gui.main.markets.trades.charts.volume;
+
+import javafx.scene.Group;
+import javafx.scene.control.Tooltip;
+import javafx.scene.layout.Region;
+import javafx.util.StringConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class VolumeBar extends Group {
+ private static final Logger log = LoggerFactory.getLogger(VolumeBar.class);
+
+ private String seriesStyleClass;
+ private String dataStyleClass;
+ private final StringConverter volumeStringConverter;
+
+ private final Region bar = new Region();
+ private final Tooltip tooltip;
+
+ VolumeBar(String seriesStyleClass, String dataStyleClass, StringConverter volumeStringConverter) {
+ this.seriesStyleClass = seriesStyleClass;
+ this.dataStyleClass = dataStyleClass;
+ this.volumeStringConverter = volumeStringConverter;
+
+ setAutoSizeChildren(false);
+ getChildren().add(bar);
+ updateStyleClasses();
+ tooltip = new Tooltip();
+ Tooltip.install(this, tooltip);
+ }
+
+ public void setSeriesAndDataStyleClasses(String seriesStyleClass, String dataStyleClass) {
+ this.seriesStyleClass = seriesStyleClass;
+ this.dataStyleClass = dataStyleClass;
+ updateStyleClasses();
+ }
+
+ public void update(double height, double candleWidth, double accumulatedAmount) {
+ bar.resizeRelocate(-candleWidth / 2, 0, candleWidth, height);
+ tooltip.setText("Volume: " + volumeStringConverter.toString(accumulatedAmount));
+ }
+
+ private void updateStyleClasses() {
+ bar.getStyleClass().setAll("volume-bar", seriesStyleClass, dataStyleClass, "bg");
+ }
+}
\ No newline at end of file
diff --git a/gui/src/main/java/io/bitsquare/gui/main/markets/trades/charts/volume/VolumeChart.java b/gui/src/main/java/io/bitsquare/gui/main/markets/trades/charts/volume/VolumeChart.java
new file mode 100644
index 0000000000..f86466453a
--- /dev/null
+++ b/gui/src/main/java/io/bitsquare/gui/main/markets/trades/charts/volume/VolumeChart.java
@@ -0,0 +1,190 @@
+/*
+ * 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 io.bitsquare.gui.main.markets.trades.charts.volume;
+
+import io.bitsquare.gui.main.markets.trades.charts.CandleData;
+import io.bitsquare.gui.main.markets.trades.charts.price.CandleStickChart;
+import javafx.animation.FadeTransition;
+import javafx.event.ActionEvent;
+import javafx.scene.Node;
+import javafx.scene.chart.Axis;
+import javafx.scene.chart.NumberAxis;
+import javafx.scene.chart.XYChart;
+import javafx.util.Duration;
+import javafx.util.StringConverter;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class VolumeChart extends XYChart {
+ private static final Logger log = LoggerFactory.getLogger(CandleStickChart.class);
+
+ private StringConverter toolTipStringConverter;
+
+ public VolumeChart(Axis xAxis, Axis yAxis) {
+ super(xAxis, yAxis);
+ }
+
+ public final void setToolTipStringConverter(StringConverter toolTipStringConverter) {
+ this.toolTipStringConverter = toolTipStringConverter;
+ }
+
+ @Override
+ protected void layoutPlotChildren() {
+ if (getData() == null) {
+ return;
+ }
+ for (int seriesIndex = 0; seriesIndex < getData().size(); seriesIndex++) {
+ XYChart.Series series = getData().get(seriesIndex);
+ Iterator> iter = getDisplayedDataIterator(series);
+ while (iter.hasNext()) {
+ XYChart.Data item = iter.next();
+ double x = getXAxis().getDisplayPosition(getCurrentDisplayedXValue(item));
+ double y = getYAxis().getDisplayPosition(getCurrentDisplayedYValue(item));
+ Node itemNode = item.getNode();
+ CandleData candleData = (CandleData) item.getExtraValue();
+ if (itemNode instanceof VolumeBar && candleData != null) {
+ VolumeBar volumeBar = (VolumeBar) itemNode;
+ double candleWidth = -1;
+ if (getXAxis() instanceof NumberAxis) {
+ NumberAxis xa = (NumberAxis) getXAxis();
+ candleWidth = xa.getDisplayPosition(xa.getTickUnit()) * 0.90; // use 90% width between ticks
+ }
+
+ // 97 is visible chart data height if chart height is 140.
+ // So we subtract 43 form the height to get the height for the bar to the bottom.
+ // Did not find a way how to request the chart data height
+ final double height = getHeight() - 43;
+ double upperYPos = Math.min(height - 5, y); // We want min 5px height to allow tooltips
+ volumeBar.update(height - upperYPos, candleWidth, candleData.accumulatedAmount);
+ volumeBar.setLayoutX(x);
+ volumeBar.setLayoutY(upperYPos);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void dataItemChanged(XYChart.Data item) {
+ }
+
+ @Override
+ protected void dataItemAdded(XYChart.Series series, int itemIndex, XYChart.Data item) {
+ Node volumeBar = createCandle(getData().indexOf(series), item, itemIndex);
+ if (getPlotChildren().contains(volumeBar))
+ getPlotChildren().remove(volumeBar);
+
+ if (shouldAnimate()) {
+ volumeBar.setOpacity(0);
+ getPlotChildren().add(volumeBar);
+ FadeTransition ft = new FadeTransition(Duration.millis(500), volumeBar);
+ ft.setToValue(1);
+ ft.play();
+ } else {
+ getPlotChildren().add(volumeBar);
+ }
+ }
+
+ @Override
+ protected void dataItemRemoved(XYChart.Data item, XYChart.Series series) {
+ final Node node = item.getNode();
+ if (shouldAnimate()) {
+ FadeTransition ft = new FadeTransition(Duration.millis(500), node);
+ ft.setToValue(0);
+ ft.setOnFinished((ActionEvent actionEvent) -> getPlotChildren().remove(node));
+ ft.play();
+ } else {
+ getPlotChildren().remove(node);
+ }
+ }
+
+ @Override
+ protected void seriesAdded(XYChart.Series series, int seriesIndex) {
+ for (int j = 0; j < series.getData().size(); j++) {
+ XYChart.Data item = series.getData().get(j);
+ Node volumeBar = createCandle(seriesIndex, item, j);
+ if (shouldAnimate()) {
+ volumeBar.setOpacity(0);
+ getPlotChildren().add(volumeBar);
+ FadeTransition ft = new FadeTransition(Duration.millis(500), volumeBar);
+ ft.setToValue(1);
+ ft.play();
+ } else {
+ getPlotChildren().add(volumeBar);
+ }
+ }
+ }
+
+ @Override
+ protected void seriesRemoved(XYChart.Series series) {
+ for (XYChart.Data d : series.getData()) {
+ final Node volumeBar = d.getNode();
+ if (shouldAnimate()) {
+ FadeTransition ft = new FadeTransition(Duration.millis(500), volumeBar);
+ ft.setToValue(0);
+ ft.setOnFinished((ActionEvent actionEvent) -> getPlotChildren().remove(volumeBar));
+ ft.play();
+ } else {
+ getPlotChildren().remove(volumeBar);
+ }
+ }
+ }
+
+ private Node createCandle(int seriesIndex, final XYChart.Data item, int itemIndex) {
+ Node volumeBar = item.getNode();
+ if (volumeBar instanceof VolumeBar) {
+ ((VolumeBar) volumeBar).setSeriesAndDataStyleClasses("series" + seriesIndex, "data" + itemIndex);
+ } else {
+ volumeBar = new VolumeBar("series" + seriesIndex, "data" + itemIndex, toolTipStringConverter);
+ item.setNode(volumeBar);
+ }
+ return volumeBar;
+ }
+
+ @Override
+ protected void updateAxisRange() {
+ final Axis xa = getXAxis();
+ final Axis ya = getYAxis();
+ List xData = null;
+ List yData = null;
+ if (xa.isAutoRanging()) {
+ xData = new ArrayList<>();
+ }
+ if (ya.isAutoRanging())
+ yData = new ArrayList<>();
+ if (xData != null || yData != null) {
+ for (XYChart.Series series : getData()) {
+ for (XYChart.Data data : series.getData()) {
+ if (xData != null) {
+ xData.add(data.getXValue());
+ }
+ if (yData != null)
+ yData.add(data.getYValue());
+ }
+ }
+ if (xData != null) {
+ xa.invalidateRange(xData);
+ }
+ if (yData != null) {
+ ya.invalidateRange(yData);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/BuyOfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/BuyOfferView.java
index fe9fe63f61..b2b0829db4 100644
--- a/gui/src/main/java/io/bitsquare/gui/main/offer/BuyOfferView.java
+++ b/gui/src/main/java/io/bitsquare/gui/main/offer/BuyOfferView.java
@@ -17,7 +17,7 @@
package io.bitsquare.gui.main.offer;
-import io.bitsquare.btc.pricefeed.PriceFeed;
+import io.bitsquare.btc.pricefeed.PriceFeedService;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.common.view.FxmlView;
import io.bitsquare.gui.common.view.ViewLoader;
@@ -29,8 +29,8 @@ import javax.inject.Inject;
public class BuyOfferView extends OfferView {
@Inject
- public BuyOfferView(ViewLoader viewLoader, Navigation navigation, PriceFeed priceFeed, Preferences preferences) {
- super(viewLoader, navigation, priceFeed, preferences);
+ public BuyOfferView(ViewLoader viewLoader, Navigation navigation, PriceFeedService priceFeedService, Preferences preferences) {
+ super(viewLoader, navigation, priceFeedService, preferences);
}
@Override
diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/OfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/OfferView.java
index 14b49a9046..1d349dc1b9 100644
--- a/gui/src/main/java/io/bitsquare/gui/main/offer/OfferView.java
+++ b/gui/src/main/java/io/bitsquare/gui/main/offer/OfferView.java
@@ -17,7 +17,7 @@
package io.bitsquare.gui.main.offer;
-import io.bitsquare.btc.pricefeed.PriceFeed;
+import io.bitsquare.btc.pricefeed.PriceFeedService;
import io.bitsquare.common.UserThread;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.common.view.ActivatableView;
@@ -54,7 +54,7 @@ public abstract class OfferView extends ActivatableView {
private final ViewLoader viewLoader;
private final Navigation navigation;
- private final PriceFeed priceFeed;
+ private final PriceFeedService priceFeedService;
private Preferences preferences;
private final Offer.Direction direction;
private Tab takeOfferTab, createOfferTab, offerBookTab;
@@ -63,10 +63,10 @@ public abstract class OfferView extends ActivatableView {
private ChangeListener tabChangeListener;
private ListChangeListener tabListChangeListener;
- protected OfferView(ViewLoader viewLoader, Navigation navigation, PriceFeed priceFeed, Preferences preferences) {
+ protected OfferView(ViewLoader viewLoader, Navigation navigation, PriceFeedService priceFeedService, Preferences preferences) {
this.viewLoader = viewLoader;
this.navigation = navigation;
- this.priceFeed = priceFeed;
+ this.priceFeedService = priceFeedService;
this.preferences = preferences;
this.direction = (this instanceof BuyOfferView) ? Offer.Direction.BUY : Offer.Direction.SELL;
}
@@ -139,7 +139,7 @@ public abstract class OfferView extends ActivatableView {
View view;
boolean isBuy = direction == Offer.Direction.BUY;
- priceFeed.setType(isBuy ? PriceFeed.Type.ASK : PriceFeed.Type.BID);
+ priceFeedService.setType(isBuy ? PriceFeedService.Type.ASK : PriceFeedService.Type.BID);
if (viewClass == OfferBookView.class && offerBookView == null) {
view = viewLoader.load(viewClass);
diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/SellOfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/SellOfferView.java
index 230052f200..76ef5547a9 100644
--- a/gui/src/main/java/io/bitsquare/gui/main/offer/SellOfferView.java
+++ b/gui/src/main/java/io/bitsquare/gui/main/offer/SellOfferView.java
@@ -17,7 +17,7 @@
package io.bitsquare.gui.main.offer;
-import io.bitsquare.btc.pricefeed.PriceFeed;
+import io.bitsquare.btc.pricefeed.PriceFeedService;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.common.view.FxmlView;
import io.bitsquare.gui.common.view.ViewLoader;
@@ -29,8 +29,8 @@ import javax.inject.Inject;
public class SellOfferView extends OfferView {
@Inject
- public SellOfferView(ViewLoader viewLoader, Navigation navigation, PriceFeed priceFeed, Preferences preferences) {
- super(viewLoader, navigation, priceFeed, preferences);
+ public SellOfferView(ViewLoader viewLoader, Navigation navigation, PriceFeedService priceFeedService, Preferences preferences) {
+ super(viewLoader, navigation, priceFeedService, preferences);
}
@Override
diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java
index 9ace8d86ab..a59337ab49 100644
--- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java
+++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferDataModel.java
@@ -26,7 +26,7 @@ import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService;
import io.bitsquare.btc.blockchain.BlockchainService;
import io.bitsquare.btc.listeners.BalanceListener;
-import io.bitsquare.btc.pricefeed.PriceFeed;
+import io.bitsquare.btc.pricefeed.PriceFeedService;
import io.bitsquare.common.crypto.KeyRing;
import io.bitsquare.gui.Navigation;
import io.bitsquare.gui.common.model.ActivatableDataModel;
@@ -70,7 +70,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
private final User user;
private final KeyRing keyRing;
private final P2PService p2PService;
- private final PriceFeed priceFeed;
+ private final PriceFeedService priceFeedService;
final String shortOfferId;
private Navigation navigation;
private final BlockchainService blockchainService;
@@ -122,7 +122,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
@Inject
CreateOfferDataModel(OpenOfferManager openOfferManager, WalletService walletService, TradeWalletService tradeWalletService,
- Preferences preferences, User user, KeyRing keyRing, P2PService p2PService, PriceFeed priceFeed,
+ Preferences preferences, User user, KeyRing keyRing, P2PService p2PService, PriceFeedService priceFeedService,
Navigation navigation, BlockchainService blockchainService, BSFormatter formatter) {
this.openOfferManager = openOfferManager;
this.walletService = walletService;
@@ -131,7 +131,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
this.user = user;
this.keyRing = keyRing;
this.p2PService = p2PService;
- this.priceFeed = priceFeed;
+ this.priceFeedService = priceFeedService;
this.navigation = navigation;
this.blockchainService = blockchainService;
this.formatter = formatter;
@@ -187,7 +187,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
paymentAccounts.setAll(user.getPaymentAccounts());
if (!preferences.getUseStickyMarketPrice() && isTabSelected)
- priceFeed.setCurrencyCode(tradeCurrencyCode.get());
+ priceFeedService.setCurrencyCode(tradeCurrencyCode.get());
updateBalance();
}
@@ -243,7 +243,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
tradeCurrencyCode.set(this.tradeCurrency.getCode());
if (!preferences.getUseStickyMarketPrice())
- priceFeed.setCurrencyCode(tradeCurrencyCode.get());
+ priceFeedService.setCurrencyCode(tradeCurrencyCode.get());
calculateVolume();
calculateTotalToPay();
@@ -253,7 +253,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
void onTabSelected(boolean isSelected) {
this.isTabSelected = isSelected;
if (!preferences.getUseStickyMarketPrice() && isTabSelected)
- priceFeed.setCurrencyCode(tradeCurrencyCode.get());
+ priceFeedService.setCurrencyCode(tradeCurrencyCode.get());
}
///////////////////////////////////////////////////////////////////////////////////////////
@@ -306,7 +306,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
acceptedCountryCodes,
bankId,
acceptedBanks,
- priceFeed);
+ priceFeedService);
}
void onPlaceOffer(Offer offer, TransactionResultHandler resultHandler) {
@@ -328,7 +328,7 @@ class CreateOfferDataModel extends ActivatableDataModel {
paymentAccount.setSelectedTradeCurrency(tradeCurrency);
if (!preferences.getUseStickyMarketPrice())
- priceFeed.setCurrencyCode(code);
+ priceFeedService.setCurrencyCode(code);
Optional tradeCurrencyOptional = preferences.getTradeCurrenciesAsObservable().stream().filter(e -> e.getCode().equals(code)).findAny();
if (!tradeCurrencyOptional.isPresent()) {
diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java
index 9841f45a2e..e788801970 100644
--- a/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java
+++ b/gui/src/main/java/io/bitsquare/gui/main/offer/createoffer/CreateOfferView.java
@@ -208,8 +208,8 @@ public class CreateOfferView extends ActivatableViewAndModel implements ViewModel {
private final BtcValidator btcValidator;
private final P2PService p2PService;
- private PriceFeed priceFeed;
+ private PriceFeedService priceFeedService;
private Preferences preferences;
private Navigation navigation;
final BSFormatter formatter;
@@ -127,7 +127,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel errorMessageListener;
private Offer offer;
private Timer timeoutTimer;
- private PriceFeed.Type priceFeedType;
+ private PriceFeedService.Type priceFeedType;
private boolean inputIsMarketBasedPrice;
private ChangeListener useMarketBasedPriceListener;
private ChangeListener currencyCodeListener;
@@ -139,14 +139,14 @@ class CreateOfferViewModel extends ActivatableWithDataModel price.set(formatter.formatFiat(newValue));
volumeAsFiatListener = (ov, oldValue, newValue) -> volume.set(formatter.formatFiat(newValue));
- isWalletFundedListener = (ov, oldValue, newValue) -> {
- updateButtonDisableState();
- };
+ isWalletFundedListener = (ov, oldValue, newValue) -> updateButtonDisableState();
/* feeFromFundingTxListener = (ov, oldValue, newValue) -> {
updateButtonDisableState();
};*/
@@ -350,20 +348,12 @@ class CreateOfferViewModel extends ActivatableWithDataModel Utilities.openWebPage("https://www.ethereum.org/"))
- .actionButtonText("Open Ethereum web page")
- .dontShowAgainId(key, preferences)
- .show();
- } else if (newValue.equals("ETHC")) {
- new Popup().information("The EHT/ETHC fork situation carries considerable risks.\n" +
+ if (newValue.equals("ETC")) {
+ new Popup().information("The EHT/ETC fork situation carries considerable risks.\n" +
"Be sure you fully understand the situation and check out the information on the \"Ethereum Classic\" and \"Ethereum\" project web pages.\n\n" +
- "Please note, that the price is denominated as ETHC/BTC not BTC/ETHC!")
+ "Please note, that the price is denominated as ETC/BTC not BTC/ETC!")
.closeButtonText("I understand")
.onAction(() -> Utilities.openWebPage("https://ethereumclassic.github.io/"))
.actionButtonText("Open Ethereum Classic web page")
@@ -430,7 +420,7 @@ class CreateOfferViewModel extends ActivatableWithDataModel o1.getOffer().getPaymentMethod().compareTo(o2.getOffer().getPaymentMethod()));
avatarColumn.setComparator((o1, o2) -> o1.getOffer().getOwnerNodeAddress().hostName.compareTo(o2.getOffer().getOwnerNodeAddress().hostName));
- nrOfOffersLabel = new Label("Nr. of offers: -");
+ nrOfOffersLabel = new Label("");
nrOfOffersLabel.setId("num-offers");
GridPane.setHalignment(nrOfOffersLabel, HPos.LEFT);
GridPane.setVgrow(nrOfOffersLabel, Priority.NEVER);
@@ -209,6 +209,7 @@ public class OfferBookView extends ActivatableViewAndModel nrOfOffersLabel.setText("Nr. of offers: " + model.getOfferList().size());
}
@Override
@@ -254,7 +255,6 @@ public class OfferBookView extends ActivatableViewAndModel nrOfOffersLabel.setText("Nr. of offers: " + model.getOfferList().size());
model.getOfferList().addListener(offerListListener);
nrOfOffersLabel.setText("Nr. of offers: " + model.getOfferList().size());
}
@@ -478,7 +478,7 @@ public class OfferBookView extends ActivatableViewAndModel observable, Number oldValue, Number newValue) {
if (offerBookListItem != null && offerBookListItem.getOffer().getPrice() != null) {
setText(model.getPrice(offerBookListItem));
- model.priceFeed.currenciesUpdateFlagProperty().removeListener(listener);
+ model.priceFeedService.currenciesUpdateFlagProperty().removeListener(listener);
}
}
};
@@ -490,14 +490,14 @@ public class OfferBookView extends ActivatableViewAndModel observable, Number oldValue, Number newValue) {
if (offerBookListItem != null && offerBookListItem.getOffer().getOfferVolume() != null) {
setText(model.getVolume(offerBookListItem));
- model.priceFeed.currenciesUpdateFlagProperty().removeListener(listener);
+ model.priceFeedService.currenciesUpdateFlagProperty().removeListener(listener);
}
}
};
@@ -539,14 +539,14 @@ public class OfferBookView extends ActivatableViewAndModel paymentAccounts) {
- for (PaymentAccount paymentAccount : paymentAccounts) {
- if (isPaymentAccountValidForOffer(offer, paymentAccount))
- return true;
- }
- return false;
- }
-
- //TODO not tested with all combinations yet....
- static boolean isPaymentAccountValidForOffer(Offer offer, PaymentAccount paymentAccount) {
- // check if we have a matching currency
- Set paymentAccountCurrencyCodes = paymentAccount.getTradeCurrencies().stream().map(TradeCurrency::getCode).collect(Collectors.toSet());
- boolean matchesCurrencyCode = paymentAccountCurrencyCodes.contains(offer.getCurrencyCode());
- if (!matchesCurrencyCode)
- return false;
-
- // check if we have a matching payment method or if its a bank account payment method which is treated special
- if (paymentAccount instanceof CountryBasedPaymentAccount) {
- CountryBasedPaymentAccount countryBasedPaymentAccount = (CountryBasedPaymentAccount) paymentAccount;
-
- // check if we have a matching country
- boolean matchesCountryCodes = offer.getAcceptedCountryCodes() != null && countryBasedPaymentAccount.getCountry() != null &&
- offer.getAcceptedCountryCodes().contains(countryBasedPaymentAccount.getCountry().code);
- if (!matchesCountryCodes)
- return false;
-
- if (paymentAccount instanceof SepaAccount || offer.getPaymentMethod().equals(PaymentMethod.SEPA)) {
- boolean samePaymentMethod = paymentAccount.getPaymentMethod().equals(offer.getPaymentMethod());
- return samePaymentMethod;
- } else if (offer.getPaymentMethod().equals(PaymentMethod.SAME_BANK) ||
- offer.getPaymentMethod().equals(PaymentMethod.SPECIFIC_BANKS)) {
-
- checkNotNull(offer.getAcceptedBankIds(), "offer.getAcceptedBankIds() must not be null");
- if (paymentAccount instanceof SpecificBanksAccount) {
- // check if we have a matching bank
- boolean offerSideMatchesBank = offer.getAcceptedBankIds().contains(((BankAccount) paymentAccount).getBankId());
- boolean paymentAccountSideMatchesBank = ((SpecificBanksAccount) paymentAccount).getAcceptedBanks().contains(offer.getBankId());
- return offerSideMatchesBank && paymentAccountSideMatchesBank;
- } else {
- // national or same bank
- boolean matchesBank = offer.getAcceptedBankIds().contains(((BankAccount) paymentAccount).getBankId());
- return matchesBank;
- }
- } else {
- if (paymentAccount instanceof SpecificBanksAccount) {
- // check if we have a matching bank
- boolean paymentAccountSideMatchesBank = ((SpecificBanksAccount) paymentAccount).getAcceptedBanks().contains(offer.getBankId());
- return paymentAccountSideMatchesBank;
- } else if (paymentAccount instanceof SameBankAccount) {
- // check if we have a matching bank
- boolean paymentAccountSideMatchesBank = ((SameBankAccount) paymentAccount).getBankId().equals(offer.getBankId());
- return paymentAccountSideMatchesBank;
- } else {
- // national
- return true;
- }
- }
-
- } else {
- return paymentAccount.getPaymentMethod().equals(offer.getPaymentMethod());
- }
+ return PaymentAccountUtil.isAnyPaymentAccountValidForOffer(offer, user.getPaymentAccounts());
}
public boolean hasPaymentAccountForCurrency() {
diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java
index 22316c5cc5..2c09a20fd9 100644
--- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java
+++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferDataModel.java
@@ -26,14 +26,14 @@ import io.bitsquare.btc.TradeWalletService;
import io.bitsquare.btc.WalletService;
import io.bitsquare.btc.blockchain.BlockchainService;
import io.bitsquare.btc.listeners.BalanceListener;
-import io.bitsquare.btc.pricefeed.PriceFeed;
+import io.bitsquare.btc.pricefeed.PriceFeedService;
import io.bitsquare.gui.common.model.ActivatableDataModel;
import io.bitsquare.gui.main.overlays.notifications.Notification;
import io.bitsquare.gui.main.overlays.popups.Popup;
import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.locale.CurrencyUtil;
-import io.bitsquare.locale.TradeCurrency;
import io.bitsquare.payment.PaymentAccount;
+import io.bitsquare.payment.PaymentAccountUtil;
import io.bitsquare.payment.PaymentMethod;
import io.bitsquare.trade.TradeManager;
import io.bitsquare.trade.handlers.TradeResultHandler;
@@ -41,14 +41,12 @@ import io.bitsquare.trade.offer.Offer;
import io.bitsquare.user.Preferences;
import io.bitsquare.user.User;
import javafx.beans.property.*;
-import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.bitcoinj.core.Coin;
import org.bitcoinj.core.Transaction;
import org.bitcoinj.utils.ExchangeRate;
import org.bitcoinj.utils.Fiat;
-import java.util.ArrayList;
import java.util.List;
import static com.google.common.base.Preconditions.checkArgument;
@@ -65,7 +63,7 @@ class TakeOfferDataModel extends ActivatableDataModel {
final WalletService walletService;
private final User user;
private final Preferences preferences;
- private final PriceFeed priceFeed;
+ private final PriceFeedService priceFeedService;
private final BlockchainService blockchainService;
private final BSFormatter formatter;
@@ -104,14 +102,14 @@ class TakeOfferDataModel extends ActivatableDataModel {
@Inject
TakeOfferDataModel(TradeManager tradeManager, TradeWalletService tradeWalletService,
WalletService walletService, User user,
- Preferences preferences, PriceFeed priceFeed, BlockchainService blockchainService,
+ Preferences preferences, PriceFeedService priceFeedService, BlockchainService blockchainService,
BSFormatter formatter) {
this.tradeManager = tradeManager;
this.tradeWalletService = tradeWalletService;
this.walletService = walletService;
this.user = user;
this.preferences = preferences;
- this.priceFeed = priceFeed;
+ this.priceFeedService = priceFeedService;
this.blockchainService = blockchainService;
this.formatter = formatter;
@@ -139,7 +137,7 @@ class TakeOfferDataModel extends ActivatableDataModel {
// feeFromFundingTxProperty.set(FeePolicy.getMinRequiredFeeForFundingTx());
if (!preferences.getUseStickyMarketPrice() && isTabSelected)
- priceFeed.setCurrencyCode(offer.getCurrencyCode());
+ priceFeedService.setCurrencyCode(offer.getCurrencyCode());
tradeManager.checkOfferAvailability(offer,
() -> {
@@ -214,13 +212,13 @@ class TakeOfferDataModel extends ActivatableDataModel {
offer.resetState();
if (!preferences.getUseStickyMarketPrice())
- priceFeed.setCurrencyCode(offer.getCurrencyCode());
+ priceFeedService.setCurrencyCode(offer.getCurrencyCode());
}
void onTabSelected(boolean isSelected) {
this.isTabSelected = isSelected;
if (!preferences.getUseStickyMarketPrice() && isTabSelected)
- priceFeed.setCurrencyCode(offer.getCurrencyCode());
+ priceFeedService.setCurrencyCode(offer.getCurrencyCode());
}
@@ -271,17 +269,7 @@ class TakeOfferDataModel extends ActivatableDataModel {
}
ObservableList getPossiblePaymentAccounts() {
- ObservableList paymentAccounts = FXCollections.observableArrayList(new ArrayList<>());
- for (PaymentAccount paymentAccount : user.getPaymentAccounts()) {
- if (paymentAccount.getPaymentMethod().equals(offer.getPaymentMethod())) {
- for (TradeCurrency tradeCurrency : paymentAccount.getTradeCurrencies()) {
- if (tradeCurrency.getCode().equals(offer.getCurrencyCode())) {
- paymentAccounts.add(paymentAccount);
- }
- }
- }
- }
- return paymentAccounts;
+ return PaymentAccountUtil.getPossiblePaymentAccounts(offer, user.getPaymentAccounts());
}
boolean hasAcceptedArbitrators() {
diff --git a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java
index 71e532581c..1e2d99172a 100644
--- a/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java
+++ b/gui/src/main/java/io/bitsquare/gui/main/offer/takeoffer/TakeOfferView.java
@@ -192,8 +192,8 @@ public class TakeOfferView extends ActivatableViewAndModel onShowPayFundsScreen(), 200, TimeUnit.MILLISECONDS);
+ /* if (DevFlags.DEV_MODE)
+ UserThread.runAfter(() -> onShowPayFundsScreen(), 200, TimeUnit.MILLISECONDS);*/
}
@Override
@@ -268,7 +268,7 @@ public class TakeOfferView extends ActivatableViewAndModel im
final TakeOfferDataModel dataModel;
private final BtcValidator btcValidator;
private final P2PService p2PService;
- private PriceFeed priceFeed;
+ private PriceFeedService priceFeedService;
private final Navigation navigation;
final BSFormatter formatter;
@@ -107,14 +107,14 @@ class TakeOfferViewModel extends ActivatableWithDataModel im
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
- public TakeOfferViewModel(TakeOfferDataModel dataModel, BtcValidator btcValidator, P2PService p2PService, PriceFeed priceFeed,
+ public TakeOfferViewModel(TakeOfferDataModel dataModel, BtcValidator btcValidator, P2PService p2PService, PriceFeedService priceFeedService,
Navigation navigation, BSFormatter formatter) {
super(dataModel);
this.dataModel = dataModel;
this.btcValidator = btcValidator;
this.p2PService = p2PService;
- this.priceFeed = priceFeed;
+ this.priceFeedService = priceFeedService;
this.navigation = navigation;
this.formatter = formatter;
@@ -141,20 +141,12 @@ class TakeOfferViewModel extends ActivatableWithDataModel im
updateSpinnerInfo();
//TODO remove after AUGUST, 30
- String key = "ETH-ETHC-Warning";
+ String key = "ETH-ETC-Warning";
if (dataModel.getPreferences().showAgain(key) && new Date().before(new Date(2016 - 1900, Calendar.AUGUST, 30))) {
- if (dataModel.getCurrencyCode().equals("ETH")) {
- new Popup().information("The EHT/ETHC fork situation carries considerable risks.\n" +
- "Be sure you fully understand the situation and check out the information on the \"Ethereum Classic\" and \"Ethereum\" project web pages.")
- .closeButtonText("I understand")
- .onAction(() -> Utilities.openWebPage("https://www.ethereum.org/"))
- .actionButtonText("Open Ethereum web page")
- .dontShowAgainId(key, dataModel.getPreferences())
- .show();
- } else if (dataModel.getCurrencyCode().equals("ETHC")) {
- new Popup().information("The EHT/ETHC fork situation carries considerable risks.\n" +
+ if (dataModel.getCurrencyCode().equals("ETC")) {
+ new Popup().information("The EHT/ETC fork situation carries considerable risks.\n" +
"Be sure you fully understand the situation and check out the information on the \"Ethereum Classic\" and \"Ethereum\" project web pages.\n\n" +
- "Please note, that the price is denominated as ETHC/BTC not BTC/ETHC!")
+ "Please note, that the price is denominated as ETC/BTC not BTC/ETC!")
.closeButtonText("I understand")
.onAction(() -> Utilities.openWebPage("https://ethereumclassic.github.io/"))
.actionButtonText("Open Ethereum Classic web page")
diff --git a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java
index e32f63709c..f7a5891b60 100644
--- a/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java
+++ b/gui/src/main/java/io/bitsquare/gui/main/overlays/windows/OfferDetailsWindow.java
@@ -32,8 +32,10 @@ import io.bitsquare.gui.main.overlays.popups.Popup;
import io.bitsquare.gui.util.BSFormatter;
import io.bitsquare.gui.util.Layout;
import io.bitsquare.locale.BSResources;
+import io.bitsquare.locale.BankUtil;
import io.bitsquare.locale.CountryUtil;
import io.bitsquare.locale.CurrencyUtil;
+import io.bitsquare.payment.PaymentAccount;
import io.bitsquare.payment.PaymentMethod;
import io.bitsquare.trade.offer.Offer;
import io.bitsquare.user.Preferences;
@@ -194,15 +196,29 @@ public class OfferDetailsWindow extends Overlay {
addLabelTextField(gridPane, ++rowIndex, "Price:", formatter.formatPriceWithCode(price));
}
}
- if (offer.isMyOffer(keyRing) && user.getPaymentAccount(offer.getOffererPaymentAccountId()) != null)
- addLabelTextField(gridPane, ++rowIndex, "Payment account:", user.getPaymentAccount(offer.getOffererPaymentAccountId()).getAccountName());
- else
- addLabelTextField(gridPane, ++rowIndex, "Payment method:", BSResources.get(offer.getPaymentMethod().getId()));
-
+ final PaymentMethod paymentMethod = offer.getPaymentMethod();
+ final String offererPaymentAccountId = offer.getOffererPaymentAccountId();
+ final PaymentAccount paymentAccount = user.getPaymentAccount(offererPaymentAccountId);
+ final String bankId = offer.getBankId();
+ final boolean isSpecificBanks = paymentMethod.equals(PaymentMethod.SPECIFIC_BANKS);
+ final boolean isNationalBanks = paymentMethod.equals(PaymentMethod.NATIONAL_BANK);
+ if (offer.isMyOffer(keyRing) && offererPaymentAccountId != null && paymentAccount != null) {
+ addLabelTextField(gridPane, ++rowIndex, "Payment account:", paymentAccount.getAccountName());
+ } else {
+ final String method = BSResources.get(paymentMethod.getId());
+ if (isNationalBanks || isSpecificBanks) {
+ if (BankUtil.isBankIdRequired(offer.getCountryCode()))
+ addLabelTextField(gridPane, ++rowIndex, "Payment method (offerers bank ID):", method + " (" + bankId + ")");
+ else if (BankUtil.isBankNameRequired(offer.getCountryCode()))
+ addLabelTextField(gridPane, ++rowIndex, "Payment method (offerers bank name):", method + " (" + bankId + ")");
+ } else {
+ addLabelTextField(gridPane, ++rowIndex, "Payment method:", method);
+ }
+ }
if (showAcceptedBanks) {
- if (offer.getPaymentMethod().equals(PaymentMethod.SAME_BANK)) {
- addLabelTextField(gridPane, ++rowIndex, "Bank name:", acceptedBanks.get(0));
- } else if (offer.getPaymentMethod().equals(PaymentMethod.SPECIFIC_BANKS)) {
+ if (paymentMethod.equals(PaymentMethod.SAME_BANK)) {
+ addLabelTextField(gridPane, ++rowIndex, "Bank ID (e.g. BIC or SWIFT):", acceptedBanks.get(0));
+ } else if (isSpecificBanks) {
String value = Joiner.on(", ").join(acceptedBanks);
Tooltip tooltip = new Tooltip("Accepted banks: " + value);
TextField acceptedBanksTextField = addLabelTextField(gridPane, ++rowIndex, "Accepted banks:", value).second;
diff --git a/gui/src/main/java/io/bitsquare/gui/main/settings/network/NetworkSettingsView.fxml b/gui/src/main/java/io/bitsquare/gui/main/settings/network/NetworkSettingsView.fxml
index 44006d185e..d039c39762 100644
--- a/gui/src/main/java/io/bitsquare/gui/main/settings/network/NetworkSettingsView.fxml
+++ b/gui/src/main/java/io/bitsquare/gui/main/settings/network/NetworkSettingsView.fxml
@@ -32,16 +32,19 @@
-
+
-
-
+
+
+
+
+
+
+
@@ -50,20 +53,20 @@
-