initial commit. gui prototype v 0.1

This commit is contained in:
Manfred Karrer 2014-04-11 11:33:48 +02:00
parent e028928571
commit bc09937785
140 changed files with 5398 additions and 2 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
/docs
/log
/target
/bin
.idea
bitsquare.iml

202
COPYING Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific languages governing permissions and
limitations under the License.

View File

@ -1,2 +1,35 @@
www.bitsquare.io
=====
bitsquare.io
===
Bitsquare is a P2P Fiat-BTC Exchange, extensible to a generic P2P trading platform (include commodities and
cryptocurrencies)
This is just a first very basic GUI prototype with mock data.
There is only the trade process for Sell BTC and the role of the offer taker modelled yet.
Done:
* Screen for orderbook with filtering mock offers by amount, price and order type (buy, sell), other filters not impl. yet
* Screen for creating an offer (needs update)
* Screen for offer taking and payment process
* Simple storage for some filter attributes
Next steps:
* Other trade variants (Buy BTC taker, Sell BTC offerer, Sell BTC offerer)
* Arbitrator integration
* bitcoinj integration
* messaging system
* ...
The project use Java 8 and Maven.
Web: http://bitsquare.io
Whitepaper: https://docs.google.com/document/d/1d3EiWZdaM89-P6MVhS53unXv2-pDpSFsN3W4kCGXKgY/edit?pli=1
Overview: http://bitsquare.io/images/overview.png
Discussion: https://bitcointalk.org/index.php?topic=462236

BIN
design/navIcons.psd Normal file

Binary file not shown.

89
pom.xml Normal file
View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>bitsquare</groupId>
<artifactId>bitsquare</artifactId>
<version>0.1</version>
<name>BitSquare</name>
<description>A P2P Fiat-Bitcoin Exchange</description>
<url>https://www.bitsquare.io</url>
<licenses>
<license>
<name>Apache 2</name>
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<url>https://github.com/bitsquare/bitsquare</url>
</scm>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.6</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.9</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>3.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-base</artifactId>
<version>r03</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.controlsfx</groupId>
<artifactId>controlsfx</artifactId>
<version>8.0.5</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>

View File

@ -0,0 +1,51 @@
package io.bitsquare;
import com.google.inject.Guice;
import com.google.inject.Injector;
import io.bitsquare.di.BitSquareModule;
import io.bitsquare.di.GuiceFXMLLoader;
import io.bitsquare.setup.ISetup;
import io.bitsquare.setup.MockSetup;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
public class BitSquare extends Application
{
private static final Logger log = LoggerFactory.getLogger(BitSquare.class);
public static void main(String[] args)
{
launch(args);
}
@Override
public void start(Stage stage)
{
Injector injector = Guice.createInjector(new BitSquareModule());
ISetup setup = injector.getInstance(MockSetup.class);
setup.applyPersistedData();
stage.setTitle("BitSquare");
GuiceFXMLLoader loader = new GuiceFXMLLoader(injector);
try
{
Parent mainView = loader.load(BitSquare.class.getResourceAsStream("/io/bitsquare/gui/MainView.fxml"));
Scene scene = new Scene(mainView, 800, 600);
scene.getStylesheets().setAll(getClass().getResource("/io/bitsquare/gui/global.css").toExternalForm());
stage.setScene(scene);
stage.show();
} catch (IOException e)
{
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,17 @@
package io.bitsquare.btc;
import com.google.inject.Inject;
/**
* Gateway to blockchain
*/
public class BlockChainFacade
{
@Inject
public BlockChainFacade()
{
}
}

View File

@ -0,0 +1,15 @@
package io.bitsquare.btc;
import java.math.BigInteger;
public class BtcFormatter
{
public static BigInteger BTC = new BigInteger("100000000");
public static BigInteger mBTC = new BigInteger("100000");
//TODO
public static double satoshiToBTC(BigInteger satoshis)
{
return satoshis.doubleValue() / BTC.doubleValue();
}
}

View File

@ -0,0 +1,11 @@
package io.bitsquare.btc;
import java.math.BigInteger;
public class Fees
{
public static BigInteger OFFER_CREATION_FEE = new BigInteger("500000");
public static BigInteger OFFER_TAKER_FEE = OFFER_CREATION_FEE;
public static BigInteger BTC_NETWORK_FEE = new BigInteger("10000");
}

View File

@ -0,0 +1,12 @@
package io.bitsquare.btc;
import java.math.BigInteger;
public interface IWalletFacade
{
BigInteger getBalance();
boolean pay(BigInteger satoshisToPay, String destinationAddress);
KeyPair createNewAddress();
}

View File

@ -0,0 +1,33 @@
package io.bitsquare.btc;
public class KeyPair
{
private String pubKey;
private String privKey;
public KeyPair(String pubKey, String privKey)
{
this.pubKey = pubKey;
this.privKey = privKey;
}
public String getPrivKey()
{
return privKey;
}
public void setPrivKey(String privKey)
{
this.privKey = privKey;
}
public String getPubKey()
{
return pubKey;
}
public void setPubKey(String pubKey)
{
this.pubKey = pubKey;
}
}

View File

@ -0,0 +1,52 @@
package io.bitsquare.btc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigInteger;
import java.util.UUID;
/**
* Gateway to wallet
*/
public class MockWalletFacade implements IWalletFacade
{
private static final Logger log = LoggerFactory.getLogger(MockWalletFacade.class);
private BigInteger balance;
public MockWalletFacade()
{
balance = new BigInteger("100000000");
}
@Override
public BigInteger getBalance()
{
return balance;
}
@Override
public boolean pay(BigInteger satoshisToPay, String destinationAddress)
{
if (getBalance().subtract(satoshisToPay).longValue() > 0)
{
log.info("Pay " + satoshisToPay.toString() + " Satoshis to " + destinationAddress);
return true;
}
else
{
log.warn("Not enough funds in wallet for paying " + satoshisToPay.toString() + " Satoshis.");
return false;
}
}
@Override
public KeyPair createNewAddress()
{
//MOCK
return new KeyPair(UUID.randomUUID().toString(), UUID.randomUUID().toString());
}
}

View File

@ -0,0 +1,6 @@
package io.bitsquare.crypto;
public interface ICryptoFacade
{
String sign(String data);
}

View File

@ -0,0 +1,16 @@
package io.bitsquare.crypto;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MockCryptoFacade implements ICryptoFacade
{
private static final Logger log = LoggerFactory.getLogger(MockCryptoFacade.class);
@Override
public String sign(String data)
{
log.info("sign data: " + data);
return "signed contract data";
}
}

View File

@ -0,0 +1,44 @@
package io.bitsquare.di;
import com.google.inject.AbstractModule;
import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.IWalletFacade;
import io.bitsquare.btc.MockWalletFacade;
import io.bitsquare.crypto.ICryptoFacade;
import io.bitsquare.crypto.MockCryptoFacade;
import io.bitsquare.msg.IMessageFacade;
import io.bitsquare.msg.MessageFacade;
import io.bitsquare.settings.OrderBookFilterSettings;
import io.bitsquare.settings.Settings;
import io.bitsquare.setup.ISetup;
import io.bitsquare.setup.MockSetup;
import io.bitsquare.storage.IStorage;
import io.bitsquare.storage.SimpleStorage;
import io.bitsquare.trade.TradingFacade;
import io.bitsquare.trade.orderbook.IOrderBook;
import io.bitsquare.trade.orderbook.MockOrderBook;
import io.bitsquare.trade.orderbook.OrderBookFilter;
import io.bitsquare.user.User;
public class BitSquareModule extends AbstractModule
{
@Override
protected void configure()
{
bind(ISetup.class).to(MockSetup.class).asEagerSingleton();
bind(User.class).asEagerSingleton();
bind(IOrderBook.class).to(MockOrderBook.class).asEagerSingleton();
bind(IStorage.class).to(SimpleStorage.class).asEagerSingleton();
bind(Settings.class).asEagerSingleton();
bind(OrderBookFilter.class).asEagerSingleton();
bind(OrderBookFilterSettings.class).asEagerSingleton();
bind(ICryptoFacade.class).to(MockCryptoFacade.class).asEagerSingleton();
bind(IWalletFacade.class).to(MockWalletFacade.class).asEagerSingleton();
bind(BlockChainFacade.class).asEagerSingleton();
bind(IMessageFacade.class).to(MessageFacade.class).asEagerSingleton();
bind(TradingFacade.class).asEagerSingleton();
}
}

View File

@ -0,0 +1,29 @@
package io.bitsquare.di;
import com.google.inject.Injector;
import javafx.util.Callback;
/**
* A JavaFX controller factory for constructing controllers via Guice DI. To
* install this in the {@link javafx.fxml.FXMLLoader}, pass it as a parameter to
* {@link javafx.fxml.FXMLLoader#setControllerFactory(Callback)}.
* <p/>
* Once set, make sure you do <b>not</b> use the static methods on
* {@link javafx.fxml.FXMLLoader} when creating your JavaFX node.
*/
public class GuiceControllerFactory implements Callback<Class<?>, Object>
{
private Injector injector;
public GuiceControllerFactory(Injector injector)
{
this.injector = injector;
}
@Override
public Object call(Class<?> aClass)
{
return injector.getInstance(aClass);
}
}

View File

@ -0,0 +1,28 @@
package io.bitsquare.di;
import com.google.inject.Injector;
import javafx.fxml.FXMLLoader;
/**
* Guice support for fxml controllers
*/
public class GuiceFXMLLoader extends FXMLLoader
{
private static Injector injector = null;
public GuiceFXMLLoader()
{
if (GuiceFXMLLoader.injector != null)
setControllerFactory(new GuiceControllerFactory(GuiceFXMLLoader.injector));
}
public GuiceFXMLLoader(Injector injector)
{
if (GuiceFXMLLoader.injector == null)
{
GuiceFXMLLoader.injector = injector;
setControllerFactory(new GuiceControllerFactory(GuiceFXMLLoader.injector));
}
}
}

View File

@ -0,0 +1,6 @@
package io.bitsquare.gui;
public interface IChildController
{
void setNavigationController(INavigationController navigationController);
}

View File

@ -0,0 +1,19 @@
package io.bitsquare.gui;
public interface INavigationController
{
public static final String HOME = "/io/bitsquare/gui/home/HomeView.fxml";
public static final String TRADE = "/io/bitsquare/gui/trade/TradeView.fxml";
public static final String ORDERS = "/io/bitsquare/gui/orders/OrdersView.fxml";
public static final String FUNDS = "/io/bitsquare/gui/funds/FundsView.fxml";
public static final String MSG = "/io/bitsquare/gui/msg/MsgView.fxml";
public static final String HISTORY = "/io/bitsquare/gui/history/HistoryView.fxml";
public static final String SETTINGS = "/io/bitsquare/gui/settings/SettingsView.fxml";
public static final String TRADE__ORDER_BOOK = "/io/bitsquare/gui/trade/orderbook/OrderBookView.fxml";
public static final String TRADE__PROCESS = "/io/bitsquare/gui/trade/tradeprocess/TradeProcessView.fxml";
public static final String TRADE__CREATE_OFFER = "/io/bitsquare/gui/trade/offer/CreateOfferView.fxml";
IChildController navigateToView(String fxmlView, String title);
}

View File

@ -0,0 +1,165 @@
package io.bitsquare.gui;
import com.google.inject.Inject;
import io.bitsquare.BitSquare;
import io.bitsquare.di.GuiceFXMLLoader;
import io.bitsquare.gui.trade.TradeController;
import io.bitsquare.gui.util.Icons;
import io.bitsquare.settings.Settings;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.orderbook.OrderBookFilter;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import java.io.IOException;
import java.net.URL;
import java.util.Currency;
import java.util.ResourceBundle;
public class MainController implements Initializable, INavigationController
{
private Settings settings;
private OrderBookFilter orderBookFilter;
private IChildController childController;
private ToggleGroup toggleGroup;
private ToggleButton prevToggleButton;
private Image prevToggleButtonIcon;
@FXML
public Pane contentPane;
public HBox leftNavPane, rightNavPane;
@Inject
public MainController(Settings settings, OrderBookFilter orderBookFilter)
{
this.settings = settings;
this.orderBookFilter = orderBookFilter;
}
@Override
public void initialize(URL url, ResourceBundle rb)
{
toggleGroup = new ToggleGroup();
ToggleButton homeButton = addNavButton(leftNavPane, "Overview", Icons.HOME, Icons.HOME, INavigationController.HOME);
ToggleButton buyButton = addNavButton(leftNavPane, "Buy BTC", Icons.NAV_BUY, Icons.NAV_BUY_ACTIVE, INavigationController.TRADE, Direction.BUY);
ToggleButton sellButton = addNavButton(leftNavPane, "Sell BTC", Icons.NAV_SELL, Icons.NAV_SELL_ACTIVE, INavigationController.TRADE, Direction.SELL);
addNavButton(leftNavPane, "Orders", Icons.ORDERS, Icons.ORDERS, INavigationController.ORDERS);
addNavButton(leftNavPane, "History", Icons.HISTORY, Icons.HISTORY, INavigationController.HISTORY);
addNavButton(leftNavPane, "Funds", Icons.FUNDS, Icons.FUNDS, INavigationController.FUNDS);
addNavButton(leftNavPane, "Message", Icons.MSG, Icons.MSG, INavigationController.MSG);
addCurrencyComboBox();
addNavButton(rightNavPane, "Settings", Icons.SETTINGS, Icons.SETTINGS, INavigationController.SETTINGS);
sellButton.fire();
//homeButton.fire();
}
@Override
public IChildController navigateToView(String fxmlView, String title)
{
FXMLLoader loader = new GuiceFXMLLoader();
try
{
Node view = loader.load(BitSquare.class.getResourceAsStream(fxmlView));
contentPane.getChildren().setAll(view);
childController = loader.getController();
childController.setNavigationController(this);
return childController;
} catch (IOException e)
{
e.printStackTrace();
}
return null;
}
public IChildController navigateToView(String fxmlView, Direction direction)
{
childController = navigateToView(fxmlView, direction == Direction.BUY ? "Orderbook Buy" : "Orderbook Sell");
if (childController instanceof TradeController && direction != null)
{
((TradeController) childController).setDirection(direction);
}
return childController;
}
private ToggleButton addNavButton(Pane parent, String title, String iconId, String iconIdActivated, String navTarget)
{
return addNavButton(parent, title, iconId, iconIdActivated, navTarget, null);
}
private ToggleButton addNavButton(Pane parent, String title, String iconId, String iconIdActivated, String navTarget, Direction direction)
{
VBox vBox = new VBox();
ToggleButton toggleButton = new ToggleButton("", Icons.getIconImageView(iconId));
toggleButton.setPrefWidth(50);
toggleButton.setToggleGroup(toggleGroup);
Label titleLabel = new Label(title);
titleLabel.setPrefWidth(50);
toggleButton.setId("nav-button");
titleLabel.setId("nav-button-label");
vBox.getChildren().setAll(toggleButton, titleLabel);
parent.getChildren().add(vBox);
toggleButton.setOnAction(e -> {
if (prevToggleButton != null)
{
((ImageView) (prevToggleButton.getGraphic())).setImage(prevToggleButtonIcon);
}
prevToggleButtonIcon = ((ImageView) (toggleButton.getGraphic())).getImage();
((ImageView) (toggleButton.getGraphic())).setImage(Icons.getIconImage(iconIdActivated));
if (childController instanceof TradeController && direction != null)
{
((TradeController) childController).setDirection(direction);
}
else
navigateToView(navTarget, direction);
prevToggleButton = toggleButton;
});
return toggleButton;
}
private void addCurrencyComboBox()
{
Pane holder = new Pane();
ComboBox currencyComboBox = new ComboBox(FXCollections.observableArrayList(settings.getAllCurrencies()));
currencyComboBox.setLayoutY(10);
currencyComboBox.setId("nav-currency-combobox");
currencyComboBox.setValue(Settings.getCurrency());
currencyComboBox.valueProperty().addListener(new ChangeListener<Currency>()
{
@Override
public void changed(ObservableValue ov, Currency oldValue, Currency newValue)
{
orderBookFilter.setCurrency(newValue);
settings.setCurrency(newValue);
}
});
holder.getChildren().add(currencyComboBox);
rightNavPane.getChildren().add(holder);
}
}

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<AnchorPane id="root-pane" minHeight="300" minWidth="400" prefHeight="600" prefWidth="800"
stylesheets="/io/bitsquare/gui/global.css" xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/8" fx:controller="io.bitsquare.gui.MainController">
<children>
<HBox fx:id="leftNavPane" spacing="20" AnchorPane.leftAnchor="10" AnchorPane.topAnchor="5"/>
<HBox fx:id="rightNavPane" spacing="10" AnchorPane.rightAnchor="10" AnchorPane.topAnchor="5"/>
<AnchorPane id="content-pane" fx:id="contentPane" AnchorPane.bottomAnchor="0" AnchorPane.leftAnchor="0"
AnchorPane.rightAnchor="0" AnchorPane.topAnchor="60"/>
</children>
</AnchorPane>

View File

@ -0,0 +1,37 @@
package io.bitsquare.gui.components;
import javafx.event.ActionEvent;
import javafx.scene.control.ToggleButton;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class BuySellSwitch extends ToggleButton
{
private static Image buyIcon = new Image(BuySellSwitch.class.getResourceAsStream("/images/buy.png"));
private static Image sellIcon = new Image(BuySellSwitch.class.getResourceAsStream("/images/sell.png"));
public BuySellSwitch(String label)
{
super(label);
ImageView iconImageView = new ImageView(buyIcon);
//setClip(iconImageView);
setGraphic(iconImageView);
addEventHandler(ActionEvent.ACTION, e -> {
if (isSelected())
{
setText("SELL");
iconImageView.setImage(sellIcon);
}
else
{
setText("BUY");
iconImageView.setImage(buyIcon);
}
});
}
}

View File

@ -0,0 +1,23 @@
package io.bitsquare.gui.components;
import javafx.scene.layout.Pane;
public class HSpacer extends Pane
{
public HSpacer()
{
}
public HSpacer(double width)
{
setPrefWidth(width);
}
@Override
protected double computePrefWidth(double width)
{
return getPrefWidth();
}
}

View File

@ -0,0 +1,11 @@
package io.bitsquare.gui.components;
import javafx.scene.control.ScrollPane;
public class NoFocusScrollPane extends ScrollPane
{
public void requestFocus()
{
// prevent focus
}
}

View File

@ -0,0 +1,66 @@
package io.bitsquare.gui.components;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.TableView;
/**
* This class allows to specify a percentage for the width of the column of a
* TableView.
*
* @author twasyl
*/
public class PTableColumn<S, T> extends javafx.scene.control.TableColumn<S, T>
{
private final DoubleProperty percentageWidth = new SimpleDoubleProperty(0);
public PTableColumn()
{
tableViewProperty().addListener(new ChangeListener<TableView<S>>()
{
@Override
public void changed(ObservableValue<? extends TableView<S>> ov, TableView<S> t, TableView<S> t1)
{
if (PTableColumn.this.prefWidthProperty().isBound())
{
PTableColumn.this.prefWidthProperty().unbind();
}
if (percentageWidth.get() != 0)
{
PTableColumn.this.prefWidthProperty().bind(t1.widthProperty().multiply(percentageWidth));
}
else
{
double tempPercentageWidthLeft = 1;
for (int i = 0; i < t1.getColumns().size(); i++)
{
tempPercentageWidthLeft -= ((PTableColumn) t1.getColumns().get(i)).getPercentageWidth();
}
PTableColumn.this.prefWidthProperty().bind(t1.widthProperty().multiply(tempPercentageWidthLeft));
}
}
});
}
public final DoubleProperty percentageWidthProperty()
{
return percentageWidth;
}
public final double getPercentageWidth()
{
return this.percentageWidthProperty().get();
}
public final void setPercentageWidth(double value) throws IllegalArgumentException
{
if (value >= 0 && value <= 1)
this.percentageWidthProperty().set(value);
else
throw new IllegalArgumentException(String.format("The provided percentage width is not between 0.0 and 1.0. Value is: %1$s", value));
}
}

View File

@ -0,0 +1,23 @@
package io.bitsquare.gui.components;
import javafx.scene.layout.Pane;
public class VSpacer extends Pane
{
public VSpacer()
{
}
public VSpacer(double height)
{
setPrefHeight(height);
}
@Override
protected double computePrefHeight(double width)
{
return getPrefHeight();
}
}

View File

@ -0,0 +1,32 @@
package io.bitsquare.gui.components.processbar;
import javafx.scene.control.Control;
import javafx.scene.control.Skin;
import java.util.List;
public class ProcessStepBar<T> extends Control
{
private List<ProcessStepItem> processStepItems;
public ProcessStepBar(List<ProcessStepItem> processStepItems)
{
this.processStepItems = processStepItems;
}
@Override
protected Skin<?> createDefaultSkin()
{
return new ProcessStepBarSkin<>(this);
}
List<ProcessStepItem> getProcessStepItems()
{
return processStepItems;
}
public void next()
{
((ProcessStepBarSkin) getSkin()).next();
}
}

View File

@ -0,0 +1,201 @@
package io.bitsquare.gui.components.processbar;
import com.sun.javafx.scene.control.behavior.BehaviorBase;
import com.sun.javafx.scene.control.behavior.KeyBinding;
import com.sun.javafx.scene.control.skin.BehaviorSkinBase;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.layout.Border;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class ProcessStepBarSkin<T> extends BehaviorSkinBase<ProcessStepBar<T>, BehaviorBase<ProcessStepBar<T>>>
{
LabelWithBorder currentLabelWithBorder;
LabelWithBorder prevLabelWithBorder;
final ProcessStepBar<T> controller;
int index;
List<LabelWithBorder> labelWithBorders;
public ProcessStepBarSkin(final ProcessStepBar<T> control)
{
super(control, new BehaviorBase<>(control, Collections.<KeyBinding>emptyList()));
controller = getSkinnable();
int i = 0;
labelWithBorders = new ArrayList<>();
int size = controller.getProcessStepItems().size();
for (Iterator<ProcessStepItem> iterator = controller.getProcessStepItems().iterator(); iterator.hasNext(); )
{
ProcessStepItem processStepItem = iterator.next();
LabelWithBorder labelWithBorder = new LabelWithBorder(processStepItem, i == 0, i == size - 1);
getChildren().add(labelWithBorder);
labelWithBorders.add(labelWithBorder);
if (i == 0)
currentLabelWithBorder = prevLabelWithBorder = labelWithBorder;
i++;
}
currentLabelWithBorder.select();
}
public void next()
{
index++;
prevLabelWithBorder.deSelect();
if (index < labelWithBorders.size())
{
currentLabelWithBorder = labelWithBorders.get(index);
currentLabelWithBorder.select();
prevLabelWithBorder = currentLabelWithBorder;
}
}
@Override
protected void layoutChildren(double x, double y, double width, double height)
{
double distance = 10;
double padding = 50;
for (int i = 0; i < getChildren().size(); i++)
{
Node node = getChildren().get(i);
double newWidth = snapSize(node.prefWidth(height)) + padding;
double newHeight = snapSize(node.prefHeight(-1) + 10);
if (i > 0)
x = snapPosition(x - ((LabelWithBorder) node).getArrowWidth());
x = snapPosition(x);
y = snapPosition(y);
node.resize(newWidth, newHeight);
node.relocate(x, y);
x += newWidth + distance;
}
}
public static class LabelWithBorder extends Label
{
private final double arrowWidth = 10;
private final double arrowHeight = 30;
private ProcessStepItem processStepItem;
private boolean isFirst;
private boolean isLast;
double borderWidth = 1;
public LabelWithBorder(ProcessStepItem processStepItem, boolean isFirst, boolean isLast)
{
super(processStepItem.getLabel());
this.processStepItem = processStepItem;
this.isFirst = isFirst;
this.isLast = isLast;
setAlignment(Pos.CENTER);
setTextFill(Color.GRAY);
setStyle("-fx-font-size: 14");
this.setShape(createButtonShape());
BorderStroke borderStroke = new BorderStroke(Color.LIGHTGRAY, BorderStrokeStyle.SOLID, null,
new BorderWidths(borderWidth, borderWidth, borderWidth, borderWidth), Insets.EMPTY);
this.setBorder(new Border(borderStroke));
}
public void select()
{
BorderStroke borderStroke = new BorderStroke(processStepItem.getColor(), BorderStrokeStyle.SOLID, null,
new BorderWidths(borderWidth, borderWidth, borderWidth, borderWidth), Insets.EMPTY);
this.setBorder(new Border(borderStroke));
setTextFill(processStepItem.getColor());
}
public void deSelect()
{
/*BorderStroke borderStroke = new BorderStroke(Color.GRAY, BorderStrokeStyle.SOLID, null,
new BorderWidths(borderWidth, borderWidth, borderWidth, borderWidth), Insets.EMPTY);
this.setBorder(new Border(borderStroke));
setTextFill(Color.GRAY); */
}
public double getArrowWidth()
{
return arrowWidth;
}
private Path createButtonShape()
{
// build the following shape (or home without left arrow)
// --------
// \ \
// / /
// --------
Path path = new Path();
// begin in the upper left corner
MoveTo e1 = new MoveTo(0, 0);
path.getElements().add(e1);
// draw a horizontal line that defines the width of the shape
HLineTo e2 = new HLineTo();
// bind the width of the shape to the width of the button
e2.xProperty().bind(this.widthProperty().subtract(arrowWidth));
path.getElements().add(e2);
if (!isLast)
{
// draw upper part of right arrow
LineTo e3 = new LineTo();
// the x endpoint of this line depends on the x property of line e2
e3.xProperty().bind(e2.xProperty().add(arrowWidth));
e3.setY(arrowHeight / 2.0);
path.getElements().add(e3);
}
// draw lower part of right arrow
LineTo e4 = new LineTo();
// the x endpoint of this line depends on the x property of line e2
e4.xProperty().bind(e2.xProperty());
e4.setY(arrowHeight);
path.getElements().add(e4);
// draw lower horizontal line
HLineTo e5 = new HLineTo(0);
path.getElements().add(e5);
if (!isFirst)
{
LineTo e6 = new LineTo(arrowWidth, arrowHeight / 2.0);
path.getElements().add(e6);
}
// close path
ClosePath e7 = new ClosePath();
path.getElements().add(e7);
// this is a dummy color to fill the shape, it won't be visible
path.setFill(Color.BLACK);
return path;
}
}
}

View File

@ -0,0 +1,42 @@
package io.bitsquare.gui.components.processbar;
import javafx.scene.paint.Paint;
public class ProcessStepItem
{
private String label;
private Paint color;
private boolean progressIndicator;
public ProcessStepItem(String label)
{
this(label, Paint.valueOf("#000000"), false);
}
public ProcessStepItem(String label, Paint color)
{
this(label, color, false);
}
public ProcessStepItem(String label, Paint color, boolean hasProgressIndicator)
{
this.label = label;
this.color = color;
this.progressIndicator = hasProgressIndicator;
}
public String getLabel()
{
return label;
}
public Paint getColor()
{
return color;
}
public boolean hasProgressIndicator()
{
return progressIndicator;
}
}

View File

@ -0,0 +1,84 @@
package io.bitsquare.gui.components.processbar;
import io.bitsquare.gui.util.Utils;
import javafx.animation.AnimationTimer;
import javafx.scene.control.Button;
import javafx.scene.control.Control;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.Pane;
import java.util.ArrayList;
import java.util.List;
public class ProcessStepsBuilder
{
protected int index = 0;
private Control previousControl;
private Pane controlHolder;
protected Object controller;
protected List<ProcessStepItem> processStepItems = new ArrayList();
protected ProcessStepBar<String> processStepBar;
public void build(Pane processStepBarHolder, Pane controlHolder, Object controller)
{
this.controlHolder = controlHolder;
this.controller = controller;
fillProcessStepItems();
processStepBar = new ProcessStepBar(processStepItems);
processStepBar.relocate(10, 10);
processStepBarHolder.getChildren().add(processStepBar);
update();
}
public void next()
{
index++;
update();
processStepBar.next();
}
// template
protected void fillProcessStepItems()
{
// to be defined in subclasses
}
protected void update()
{
if (index < processStepItems.size())
{
ProcessStepItem processStepItem = processStepItems.get(index);
if (previousControl != null)
controlHolder.getChildren().remove(previousControl);
if (processStepItem.hasProgressIndicator())
{
final ProgressIndicator progressIndicator = new ProgressIndicator();
progressIndicator.setProgress(-1.0);
progressIndicator.setPrefSize(30.0, 30.0);
controlHolder.getChildren().add(progressIndicator);
previousControl = progressIndicator;
// TODO
// mock simulate network delay
Utils.setTimeout(100, (AnimationTimer animationTimer) -> {
next();
return null;
});
}
else
{
final Button button = new Button(processStepItem.getLabel());
button.setOnAction(e -> next());
controlHolder.getChildren().add(button);
previousControl = button;
}
}
}
}

View File

@ -0,0 +1,26 @@
package io.bitsquare.gui.funds;
import io.bitsquare.gui.IChildController;
import io.bitsquare.gui.INavigationController;
import javafx.fxml.Initializable;
import java.net.URL;
import java.util.ResourceBundle;
public class FundsController implements Initializable, IChildController
{
private INavigationController navigationController;
@Override
public void initialize(URL url, ResourceBundle rb)
{
}
@Override
public void setNavigationController(INavigationController navigationController)
{
this.navigationController = navigationController;
}
}

View File

@ -0,0 +1,6 @@
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<Pane fx:controller="io.bitsquare.gui.funds.FundsController"
xmlns:fx="http://javafx.com/fxml">
<Label text="Wallet"/>
</Pane>

View File

@ -0,0 +1,109 @@
#root-pane {
-fx-background-color: #dddddd;
}
#content-pane {
-fx-background-color: #f4f4f4;
}
/* main nav */
#nav-button {
-fx-background-color: transparent;
}
#nav-button-label {
-fx-font-size: 10;
-fx-alignment: center;
-fx-padding: -10;
}
#nav-buy-button {
-fx-background-color: transparent;
}
#nav-buy-button-label {
-fx-font-size: 10;
-fx-alignment: center;
-fx-padding: -10;
}
#nav-sell-button {
-fx-background-color: transparent;
}
#nav-sell-button-label {
-fx-font-size: 10;
-fx-alignment: center;
-fx-padding: -10;
}
#nav-currency-combobox {
}
/* */
#orderbook-table .table-cell {
-fx-alignment: center;
}
#orderbook-table .column-header .label {
-fx-alignment: center;
}
#orderbook-table .focus {
-fx-alignment: center;
}
#feedback-text {
-fx-font-size: 10;
}
/* forms */
#form-header-text {
-fx-font-weight: bold;
-fx-font-size: 14;
}
#form-title {
-fx-font-weight: bold;
}
/* tabpane */
.tab-pane .tab-label {
-fx-font-size: 15;
}
.tab-pane:focused {
-fx-background-color: transparent;
}
.tab-header-area:focused {
-fx-background-color: transparent;
}
.tab:focused {
-fx-background-color: transparent;
}
/* tableview */
.table-view:focused {
-fx-background-color: transparent;
}
/* scrollpane */
.scroll-pane {
-fx-background-insets: 0;
-fx-padding: 0;
}
.scroll-pane:focused {
-fx-background-insets: 0;
}
.scroll-pane .corner {
-fx-background-insets: 0;
}

View File

@ -0,0 +1,28 @@
package io.bitsquare.gui.history;
import io.bitsquare.gui.IChildController;
import io.bitsquare.gui.INavigationController;
import javafx.fxml.Initializable;
import java.net.URL;
import java.util.ResourceBundle;
public class HistoryController implements Initializable, IChildController
{
private INavigationController navigationController;
@Override
public void initialize(URL url, ResourceBundle rb)
{
}
@Override
public void setNavigationController(INavigationController navigationController)
{
this.navigationController = navigationController;
}
}

View File

@ -0,0 +1,6 @@
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<Pane fx:controller="io.bitsquare.gui.history.HistoryController"
xmlns:fx="http://javafx.com/fxml">
<Label text="Histroy"/>
</Pane>

View File

@ -0,0 +1,32 @@
package io.bitsquare.gui.home;
import io.bitsquare.gui.IChildController;
import io.bitsquare.gui.INavigationController;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.layout.Pane;
import java.net.URL;
import java.util.ResourceBundle;
public class HomeController implements Initializable, IChildController
{
private INavigationController navigationController;
@FXML
public Pane rootContainer;
@Override
public void initialize(URL url, ResourceBundle rb)
{
}
@Override
public void setNavigationController(INavigationController navigationController)
{
this.navigationController = navigationController;
}
}

View File

@ -0,0 +1,4 @@
<?import javafx.scene.layout.VBox?>
<VBox fx:id="rootContainer" fx:controller="io.bitsquare.gui.home.HomeController" spacing="10"
xmlns:fx="http://javafx.com/fxml">
</VBox>

View File

@ -0,0 +1,50 @@
package io.bitsquare.gui.msg;
import java.util.concurrent.*;
public class MockDelay
{
public String waitForMsg(String expectedMsg)
{
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new Task(expectedMsg));
try
{
try
{
// max timeout 5 sec
return future.get(5, TimeUnit.SECONDS);
} catch (InterruptedException e)
{
e.printStackTrace();
} catch (ExecutionException e)
{
e.printStackTrace();
}
} catch (TimeoutException e)
{
System.out.println("Terminated!");
}
executor.shutdownNow();
return null;
}
}
class Task implements Callable<String>
{
private String expectedMsg;
Task(String expectedMsg)
{
this.expectedMsg = expectedMsg;
}
@Override
public String call() throws Exception
{
Thread.sleep(1000); // 1 seconds pause
return expectedMsg;
}
}

View File

@ -0,0 +1,27 @@
package io.bitsquare.gui.msg;
import io.bitsquare.gui.IChildController;
import io.bitsquare.gui.INavigationController;
import javafx.fxml.Initializable;
import java.net.URL;
import java.util.ResourceBundle;
public class MsgController implements Initializable, IChildController
{
private INavigationController navigationController;
@Override
public void initialize(URL url, ResourceBundle rb)
{
}
@Override
public void setNavigationController(INavigationController navigationController)
{
this.navigationController = navigationController;
}
}

View File

@ -0,0 +1,6 @@
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<Pane fx:controller="io.bitsquare.gui.msg.MsgController"
xmlns:fx="http://javafx.com/fxml">
<Label text="Msg"/>
</Pane>

View File

@ -0,0 +1,30 @@
package io.bitsquare.gui.orders;
import io.bitsquare.gui.IChildController;
import io.bitsquare.gui.INavigationController;
import javafx.fxml.Initializable;
import java.net.URL;
import java.util.ResourceBundle;
public class OrdersController implements Initializable, IChildController
{
private INavigationController navigationController;
@Override
public void initialize(URL url, ResourceBundle rb)
{
}
@Override
public void setNavigationController(INavigationController navigationController)
{
this.navigationController = navigationController;
}
}

View File

@ -0,0 +1,6 @@
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<Pane fx:controller="io.bitsquare.gui.orders.OrdersController"
xmlns:fx="http://javafx.com/fxml">
<Label text="Orders"/>
</Pane>

View File

@ -0,0 +1,30 @@
package io.bitsquare.gui.settings;
import io.bitsquare.gui.IChildController;
import io.bitsquare.gui.INavigationController;
import javafx.fxml.Initializable;
import java.net.URL;
import java.util.ResourceBundle;
public class SettingsController implements Initializable, IChildController
{
private INavigationController navigationController;
@Override
public void initialize(URL url, ResourceBundle rb)
{
}
@Override
public void setNavigationController(INavigationController navigationController)
{
this.navigationController = navigationController;
}
}

View File

@ -0,0 +1,6 @@
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.Pane?>
<Pane fx:controller="io.bitsquare.gui.settings.SettingsController"
xmlns:fx="http://javafx.com/fxml">
<Label text="Settings"/>
</Pane>

View File

@ -0,0 +1,89 @@
package io.bitsquare.gui.trade;
import io.bitsquare.BitSquare;
import io.bitsquare.di.GuiceFXMLLoader;
import io.bitsquare.gui.IChildController;
import io.bitsquare.gui.INavigationController;
import io.bitsquare.gui.trade.orderbook.OrderBookController;
import io.bitsquare.trade.Direction;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.Pane;
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;
public class TradeController implements Initializable, INavigationController, IChildController
{
@FXML
private TabPane tabPane;
private IChildController childController;
private boolean orderbookCreated;
private INavigationController navigationController;
private OrderBookController orderBookController;
@Override
public IChildController navigateToView(String fxmlView, String title)
{
if (fxmlView.equals(INavigationController.TRADE__ORDER_BOOK) && orderbookCreated)
{
tabPane.getSelectionModel().select(0);
return null;
}
FXMLLoader loader = new GuiceFXMLLoader();
try
{
Pane view = loader.load(BitSquare.class.getResourceAsStream(fxmlView));
childController = loader.getController();
childController.setNavigationController(this);
if (childController instanceof OrderBookController)
orderBookController = (OrderBookController) childController;
Tab tab = new Tab(title);
tab.setContent(view);
tabPane.getTabs().add(tab);
if (fxmlView.equals(INavigationController.TRADE__ORDER_BOOK))
{
tab.setClosable(false);
orderbookCreated = true;
}
tabPane.getSelectionModel().select(tabPane.getTabs().size() - 1);
return childController;
} catch (IOException e)
{
e.printStackTrace();
}
return null;
}
@Override
public void initialize(URL url, ResourceBundle rb)
{
navigateToView(INavigationController.TRADE__ORDER_BOOK, "Orderbook");
}
@Override
public void setNavigationController(INavigationController navigationController)
{
this.navigationController = navigationController;
}
public void setDirection(Direction direction)
{
tabPane.getSelectionModel().select(0);
orderBookController.setDirection(direction);
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.AnchorPane?>
<TabPane fx:id="tabPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="io.bitsquare.gui.trade.TradeController"/>

View File

@ -0,0 +1,271 @@
package io.bitsquare.gui.trade.offer;
import com.google.inject.Inject;
import io.bitsquare.gui.IChildController;
import io.bitsquare.gui.INavigationController;
import io.bitsquare.gui.util.Converter;
import io.bitsquare.gui.util.Formatter;
import io.bitsquare.settings.OrderBookFilterSettings;
import io.bitsquare.settings.Settings;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Offer;
import io.bitsquare.trade.OfferConstraints;
import io.bitsquare.trade.TradingFacade;
import io.bitsquare.trade.orderbook.MockOrderBook;
import io.bitsquare.trade.orderbook.OrderBookFilter;
import io.bitsquare.user.User;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URL;
import java.util.ArrayList;
import java.util.Currency;
import java.util.ResourceBundle;
import java.util.UUID;
public class CreateOfferController implements Initializable, IChildController
{
private static final Logger log = LoggerFactory.getLogger(CreateOfferController.class);
private INavigationController navigationController;
private TradingFacade tradingFacade;
private OrderBookFilterSettings orderBookFilterSettings;
private Settings settings;
private User user;
private double filterPaneItemOffset;
private Direction direction;
@FXML
public AnchorPane holderPane;
@FXML
public Pane detailsPane;
@FXML
public Label buyLabel;
@FXML
public TextField volume;
@FXML
public ImageView directionImageView;
@FXML
public TextField amount;
@FXML
public TextField price;
@FXML
public TextField minAmount;
@FXML
public Button placeOfferButton;
@Inject
public CreateOfferController(TradingFacade tradingFacade, OrderBookFilterSettings orderBookFilterSettings, Settings settings, User user)
{
this.tradingFacade = tradingFacade;
this.orderBookFilterSettings = orderBookFilterSettings;
this.settings = settings;
this.user = user;
}
@Override
public void initialize(URL url, ResourceBundle rb)
{
createFilterPane();
amount.textProperty().addListener(new ChangeListener<String>()
{
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
{
setVolume();
}
});
price.textProperty().addListener(new ChangeListener<String>()
{
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
{
setVolume();
}
});
placeOfferButton.setOnAction(e -> {
// TODO not impl yet. use mocks
OfferConstraints offerConstraints = new MockOrderBook(settings).getRandomOfferConstraints();
Offer offer = new Offer(UUID.randomUUID(),
direction,
Converter.convertToDouble(price.getText()),
Converter.convertToDouble(amount.getText()),
Converter.convertToDouble(minAmount.getText()),
settings.getCurrency(),
user,
offerConstraints);
tradingFacade.placeNewOffer(offer);
TabPane tabPane = ((TabPane) (holderPane.getParent().getParent()));
tabPane.getTabs().remove(tabPane.getSelectionModel().getSelectedItem());
navigationController.navigateToView(INavigationController.TRADE__ORDER_BOOK, "Orderbook");
});
}
@Override
public void setNavigationController(INavigationController navigationController)
{
this.navigationController = navigationController;
}
public void setOrderBookFilter(OrderBookFilter orderBookFilter)
{
direction = orderBookFilter.getDirection();
amount.setText(Formatter.formatPrice(orderBookFilter.getAmount()));
minAmount.setText(Formatter.formatPrice(orderBookFilter.getAmount()));
price.setText(Formatter.formatPrice(orderBookFilter.getPrice()));
configDirection();
}
private void configDirection()
{
String iconPath;
String buyLabelText;
if (direction == Direction.BUY)
{
iconPath = "/images/buy.png";
buyLabelText = "BUY";
}
else
{
iconPath = "/images/sell.png";
buyLabelText = "SELL";
}
Image icon = new Image(getClass().getResourceAsStream(iconPath));
directionImageView.setImage(icon);
buyLabel.setText(buyLabelText);
}
private void createFilterPane()
{
filterPaneItemOffset = 30;
ArrayList<Currency> currencies = orderBookFilterSettings.getCurrencies();
Currency currency = orderBookFilterSettings.getCurrency();
ComboBox currencyComboBox = createCurrencyItem("Currency: ", currency, currencies);
currencyComboBox.valueProperty().addListener(new ChangeListener<Currency>()
{
@Override
public void changed(ObservableValue ov, Currency oldValue, Currency newValue)
{
orderBookFilterSettings.setCurrency(newValue);
}
});
Label bankLabel = createFilterItem("Bank transfer types: ", "SEPA, OKPAY");
Label countriesLabel = createFilterItem("Countries: ", "DE, GB, AT");
Label languagesLabel = createFilterItem("Languages: ", "DE, EN");
Label arbitratorsLabel = createFilterItem("Arbitrators: ", "Paysty, BitRated");
Label identityLabel = createFilterItem("Identity verifications: ", "Passport, Google+, Facebook, Skype");
TextField collateralLabel = createCollateralItem("Collateral (%): ", 10);
}
private ComboBox createCurrencyItem(String labelText, Currency currency, ArrayList<Currency> currencies)
{
final Separator separator = new Separator();
separator.setPrefWidth(380);
separator.setLayoutY(0 + filterPaneItemOffset);
separator.setLayoutX(0);
final Label label = new Label(labelText);
label.setLayoutY(10 + filterPaneItemOffset);
ObservableList<Currency> options = FXCollections.observableArrayList(currencies);
final ComboBox comboBox = new ComboBox(options);
comboBox.setLayoutX(70);
comboBox.setLayoutY(5 + filterPaneItemOffset);
comboBox.setValue(currency);
detailsPane.getChildren().addAll(separator, label, comboBox);
filterPaneItemOffset += 40;
return comboBox;
}
private Label createFilterItem(String labelText, String valueText)
{
final Separator separator = new Separator();
separator.setPrefWidth(380);
separator.setLayoutY(0 + filterPaneItemOffset);
separator.setLayoutX(0);
final Label label = new Label(labelText + valueText);
label.setLayoutY(10 + filterPaneItemOffset);
label.setPrefWidth(310);
Tooltip tooltip = new Tooltip(valueText);
label.setTooltip(tooltip);
final Button edit = new Button("Edit");
edit.setPrefWidth(50);
edit.setLayoutX(330);
edit.setLayoutY(5 + filterPaneItemOffset);
detailsPane.getChildren().addAll(separator, label, edit);
filterPaneItemOffset += 40;
return label;
}
private TextField createCollateralItem(String labelText, double collateral)
{
final Separator separator = new Separator();
separator.setPrefWidth(380);
separator.setLayoutY(0 + filterPaneItemOffset);
separator.setLayoutX(0);
final Label label = new Label(labelText);
label.setLayoutY(10 + filterPaneItemOffset);
label.setPrefWidth(310);
final TextField collateralValue = new TextField(Double.toString(collateral));
collateralValue.setLayoutX(90);
collateralValue.setLayoutY(5 + filterPaneItemOffset);
collateralValue.setPrefWidth(50);
detailsPane.getChildren().addAll(separator, label, collateralValue);
filterPaneItemOffset += 40;
return collateralValue;
}
private double textInputToNumber(String oldValue, String newValue)
{
//TODO use regex.... or better custom textfield component
double d = 0.0;
if (!newValue.equals(""))
{
d = Converter.convertToDouble(newValue);
if (d == Double.NEGATIVE_INFINITY)
{
amount.setText(oldValue);
d = Converter.convertToDouble(oldValue);
}
}
return d;
}
private void setVolume()
{
double a = textInputToNumber(amount.getText(), amount.getText());
double p = textInputToNumber(price.getText(), price.getText());
volume.setText(Formatter.formatPrice(a * p));
}
}

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.Font?>
<AnchorPane fx:id="holderPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="io.bitsquare.gui.trade.offer.CreateOfferController">
<children>
<HBox prefHeight="22.0" AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="10.0">
<children>
<ImageView fx:id="directionImageView"/>
<Label fx:id="buyLabel" text="Buy">
<padding>
<Insets left="4.0" right="4.0" top="5.0"/>
</padding>
</Label>
<TextField fx:id="amount" prefHeight="26.0" prefWidth="70.0" alignment="CENTER_RIGHT">
<HBox.margin>
<Insets left="0.0"/>
</HBox.margin>
</TextField>
<Label text="BTC for">
<padding>
<Insets left="4.0" right="4.0" top="5.0"/>
</padding>
</Label>
<TextField fx:id="price" prefHeight="26.0" prefWidth="70.0" alignment="CENTER_RIGHT"/>
<Label text="EUR =">
<padding>
<Insets left="4.0" right="4.0" top="5.0"/>
</padding>
</Label>
<TextField fx:id="volume" prefHeight="26.0" prefWidth="70.0" alignment="CENTER_RIGHT" editable="false"/>
<Label prefHeight="21.0" text="EUR in total (Min. Amount:">
<padding>
<Insets left="4.0" right="4.0" top="5.0"/>
</padding>
<HBox.margin>
<Insets/>
</HBox.margin>
</Label>
<TextField fx:id="minAmount" prefHeight="26.0" prefWidth="70.0" alignment="CENTER_RIGHT"/>
<Label text="BTC)">
<padding>
<Insets left="4.0" right="4.0" top="5.0"/>
</padding>
</Label>
</children>
</HBox>
<Pane fx:id="detailsPane" AnchorPane.topAnchor="50.0"
AnchorPane.bottomAnchor="10.0" AnchorPane.leftAnchor="10.0">
<Label text="Offer details:">
<font>
<Font size="18.0"/>
</font>
</Label>
</Pane>
<Label text="Place offer" AnchorPane.leftAnchor="10.0" AnchorPane.topAnchor="380.0">
<font>
<Font size="18.0"/>
</font>
</Label>
<Separator AnchorPane.leftAnchor="10.0" AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="405.0"/>
<Label text="Offer fee: 0.01 BTC" AnchorPane.leftAnchor="10.0" AnchorPane.topAnchor="415.0"/>
<Button fx:id="placeOfferButton" text="Place offer" AnchorPane.leftAnchor="10.0" AnchorPane.topAnchor="440.0"/>
</children>
</AnchorPane>

View File

@ -0,0 +1,242 @@
package io.bitsquare.gui.trade.orderbook;
import com.google.inject.Inject;
import io.bitsquare.gui.IChildController;
import io.bitsquare.gui.INavigationController;
import io.bitsquare.gui.trade.offer.CreateOfferController;
import io.bitsquare.gui.trade.tradeprocess.TradeProcessController;
import io.bitsquare.gui.util.Converter;
import io.bitsquare.gui.util.Formatter;
import io.bitsquare.gui.util.Icons;
import io.bitsquare.settings.Settings;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.orderbook.IOrderBook;
import io.bitsquare.trade.orderbook.MockOrderBook;
import io.bitsquare.trade.orderbook.OrderBookFilter;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.util.Arrays;
import java.util.ResourceBundle;
public class OrderBookController implements Initializable, IChildController
{
private INavigationController navigationController;
private IOrderBook orderBook;
private Settings settings;
private OrderBookListItem selectedOrderBookListItem;
private final OrderBookFilter orderBookFilter;
private Button createOfferButton;
private Image buyIcon = Icons.getIconImage(Icons.BUY);
private Image sellIcon = Icons.getIconImage(Icons.SELL);
@FXML
public AnchorPane holderPane;
@FXML
public HBox topHBox;
@FXML
private Button tradeButton;
@FXML
public TextField volume, amount, price;
@FXML
public Pane filterPane;
@FXML
public TableView<OrderBookListItem> orderBookTable;
@FXML
public TableColumn priceColumn, amountColumn, volumeColumn;
@FXML
private ImageView tradeButtonImageView;
@Inject
public OrderBookController(IOrderBook orderBook, OrderBookFilter orderBookFilter, Settings settings)
{
this.orderBook = orderBook;
this.orderBookFilter = orderBookFilter;
this.settings = settings;
}
@Override
public void initialize(URL url, ResourceBundle rb)
{
orderBookFilter.getCurrencyProperty().addListener(new ChangeListener<String>()
{
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
{
updateOfferList();
}
});
createFilterPane();
updateOfferList();
amount.textProperty().addListener(new ChangeListener<String>()
{
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
{
orderBookFilter.setAmount(textInputToNumber(oldValue, newValue));
updateOfferList();
updateVolume();
}
});
price.textProperty().addListener(new ChangeListener<String>()
{
@Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue)
{
orderBookFilter.setPrice(textInputToNumber(oldValue, newValue));
updateOfferList();
updateVolume();
}
});
orderBookTable.getSelectionModel().selectedItemProperty().addListener((observableValue, oldValue, newValue) -> {
selectedOrderBookListItem = orderBookTable.getSelectionModel().getSelectedItem();
tradeButton.setDisable(selectedOrderBookListItem == null);
});
tradeButton.setOnAction(e -> openTradeTab(selectedOrderBookListItem));
tradeButton.setDisable(true);
tradeButton.setDefaultButton(true);
}
@Override
public void setNavigationController(INavigationController navigationController)
{
this.navigationController = navigationController;
}
public void setDirection(Direction direction)
{
orderBookTable.getSelectionModel().clearSelection();
tradeButton.setDisable(true);
price.setText("");
String title;
Image icon;
if (direction == Direction.SELL)
{
title = "SELL";
icon = sellIcon;
}
else
{
title = "BUY";
icon = buyIcon;
}
tradeButton.setText(title);
tradeButtonImageView.setImage(icon);
orderBookFilter.setDirection(direction);
updateOfferList();
}
private void openTradeTab(OrderBookListItem orderBookListItem)
{
String title = orderBookListItem.getOffer().getDirection() == Direction.BUY ? "Trade: Sell Bitcoin" : "Trade: Buy Bitcoin";
TradeProcessController tradeProcessController = (TradeProcessController) navigationController.navigateToView(INavigationController.TRADE__PROCESS, title);
double requestedAmount = orderBookListItem.getOffer().getAmount();
if (!amount.getText().equals(""))
requestedAmount = Converter.convertToDouble(amount.getText());
tradeProcessController.initView(orderBookListItem.getOffer(), requestedAmount);
}
private void displayCreateOfferButton()
{
if (createOfferButton == null)
{
createOfferButton = new Button("Create new offer");
holderPane.setBottomAnchor(createOfferButton, 375.0);
holderPane.setLeftAnchor(createOfferButton, 200.0);
holderPane.getChildren().add(createOfferButton);
createOfferButton.setOnAction(e -> {
IChildController nextController = navigationController.navigateToView(INavigationController.TRADE__CREATE_OFFER, "Create offer");
((CreateOfferController) nextController).setOrderBookFilter(orderBookFilter);
});
}
createOfferButton.setVisible(true);
holderPane.setBottomAnchor(orderBookTable, 410.0);
}
private void updateOfferList()
{
ObservableList offers = orderBook.getFilteredList(orderBookFilter);
orderBookTable.setItems(offers);
orderBookTable.getSortOrder().add(priceColumn);
priceColumn.setSortType((orderBookFilter.getDirection() == Direction.BUY) ? TableColumn.SortType.ASCENDING : TableColumn.SortType.DESCENDING);
if (offers.size() == 0)
{
displayCreateOfferButton();
}
else if (createOfferButton != null)
{
createOfferButton.setVisible(false);
holderPane.setBottomAnchor(orderBookTable, 10.0);
}
}
private void createFilterPane()
{
MockOrderBook mockOrderBook = new MockOrderBook(settings);
orderBookFilter.setOfferConstraints(mockOrderBook.getRandomOfferConstraints());
OrderBookFilterTextItemBuilder.build(filterPane, "Bank transfer types: ", orderBookFilter.getOfferConstraints().getBankTransferTypes(), settings.getAllBankTransferTypes());
OrderBookFilterTextItemBuilder.build(filterPane, "Countries: ", orderBookFilter.getOfferConstraints().getCountries(), settings.getAllCountries());
OrderBookFilterTextItemBuilder.build(filterPane, "Languages: ", orderBookFilter.getOfferConstraints().getLanguages(), settings.getAllLanguages());
OrderBookFilterTextItemBuilder.build(filterPane, "Collateral: ", Arrays.asList(String.valueOf(orderBookFilter.getOfferConstraints().getCollateral())), settings.getAllCollaterals());
OrderBookFilterTextItemBuilder.build(filterPane, "Arbitrator: ", Arrays.asList(orderBookFilter.getOfferConstraints().getArbitrator()), settings.getAllArbitrators());
}
private double textInputToNumber(String oldValue, String newValue)
{
//TODO use regex.... or custom textfield component
double d = 0.0;
if (!newValue.equals(""))
{
try
{
DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance(Settings.getLocale());
d = decimalFormat.parse(newValue).doubleValue();
} catch (ParseException e)
{
amount.setText(oldValue);
d = Converter.convertToDouble(oldValue);
}
}
return d;
}
private void updateVolume()
{
double a = textInputToNumber(amount.getText(), amount.getText());
double p = textInputToNumber(price.getText(), price.getText());
volume.setText(Formatter.formatPrice(a * p));
}
}

View File

@ -0,0 +1,134 @@
package io.bitsquare.gui.trade.orderbook;
import io.bitsquare.gui.components.VSpacer;
import io.bitsquare.gui.util.Icons;
import io.bitsquare.settings.Settings;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.image.ImageView;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.Pane;
import java.util.*;
public class OrderBookFilterTextItemBuilder
{
public static void build(Pane parent, String title, List<String> values, List<String> allValues)
{
final Pane pane = new Pane();
pane.setPrefHeight(23);
final Label titleLabel = new Label(title);
titleLabel.setLayoutY(4);
titleLabel.setId("form-title");
FlowPane flowPane = new FlowPane();
double xPos = 170.0;
double yPos = 5.0;
List<String> openValues = new ArrayList<>(allValues);
openValues.removeAll(values);
ObservableList<String> observableList = FXCollections.observableArrayList(openValues);
Collections.sort(observableList);
ComboBox comboBox = new ComboBox(observableList);
comboBox.setLayoutX(xPos);
comboBox.setLayoutY(yPos);
comboBox.setClip(Icons.getIconImageView(Icons.ADD));
comboBox.setValue(Settings.getCurrency());
comboBox.valueProperty().addListener(new ChangeListener<Object>()
{
@Override
public void changed(ObservableValue ov, Object oldValue, Object newValue)
{
if (newValue != null)
{
String value;
if (newValue instanceof Currency)
value = ((Currency) newValue).getCurrencyCode();
else
value = (String) newValue;
if (flowPane.getChildren().size() > 0)
{
Pane lastItem = (Pane) flowPane.getChildren().get(flowPane.getChildren().size() - 1);
Button button = (Button) lastItem.getChildren().get(0);
button.setText(button.getText().substring(0, button.getText().length() - 2) + ", ");
}
addRemovableItem(flowPane, value + " ", observableList);
comboBox.getSelectionModel().clearSelection();
observableList.remove(newValue);
}
}
});
// combobox does not support icon (mask background with icon), so we need a graphic here
ImageView addImageView = Icons.getIconImageView(Icons.ADD);
addImageView.setLayoutX(xPos);
addImageView.setLayoutY(yPos);
addImageView.setMouseTransparent(true);
pane.getChildren().addAll(titleLabel, comboBox, addImageView);
Iterator<String> iterator = values.iterator();
for (Iterator<String> stringIterator = iterator; stringIterator.hasNext(); )
{
String value = stringIterator.next();
if (stringIterator.hasNext())
addRemovableItem(flowPane, value + ", ", observableList);
else
addRemovableItem(flowPane, value + " ", observableList);
}
parent.getChildren().addAll(pane, flowPane, new VSpacer(3), new Separator(), new VSpacer(10));
}
private static void addRemovableItem(FlowPane flowPane, String text, ObservableList<String> observableList)
{
Pane pane = new Pane();
Button icon = new Button("", Icons.getIconImageView(Icons.REMOVE));
icon.setStyle("-fx-background-color: transparent;");
icon.setPadding(new Insets(-5.0, 0.0, 0.0, 0.0));
icon.setVisible(false);
Button button = new Button(text);
button.setStyle("-fx-background-color: transparent;");
button.setPadding(new Insets(0.0, 0.0, 0.0, 0.0));
pane.setOnMouseEntered(e -> {
icon.setVisible(true);
icon.setLayoutX(button.getWidth() - 7);
});
pane.setOnMouseExited(e -> {
icon.setVisible(false);
icon.setLayoutX(0);
});
icon.setOnAction(e -> {
flowPane.getChildren().remove(button.getParent());
observableList.add(text);
Collections.sort(observableList);
if (flowPane.getChildren().size() > 0)
{
Pane lastItem = (Pane) flowPane.getChildren().get(flowPane.getChildren().size() - 1);
Button lastButton = (Button) lastItem.getChildren().get(0);
lastButton.setText(lastButton.getText().substring(0, lastButton.getText().length() - 2) + " ");
}
});
pane.getChildren().addAll(button, icon);
flowPane.getChildren().add(pane);
}
}

View File

@ -0,0 +1,49 @@
package io.bitsquare.gui.trade.orderbook;
import io.bitsquare.gui.util.Formatter;
import io.bitsquare.trade.Offer;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
* Wrapper for observable properties used by orderbook table view
*/
public class OrderBookListItem
{
private final StringProperty price = new SimpleStringProperty();
private final StringProperty amount = new SimpleStringProperty();
private final StringProperty volume = new SimpleStringProperty();
private Offer offer;
public OrderBookListItem(Offer offer)
{
this.offer = offer;
this.price.set(Formatter.formatPrice(offer.getPrice()));
this.amount.set(Formatter.formatAmountWithMinAmount(offer.getAmount(), offer.getMinAmount()));
this.volume.set(Formatter.formatVolumeWithMinVolume(offer.getVolume(), offer.getMinVolume()));
}
public Offer getOffer()
{
return offer;
}
// called form table columns
public final StringProperty priceProperty()
{
return this.price;
}
public final StringProperty amountProperty()
{
return this.amount;
}
public final StringProperty volumeProperty()
{
return this.volume;
}
}

View File

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import io.bitsquare.gui.components.HSpacer?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:id="holderPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="io.bitsquare.gui.trade.orderbook.OrderBookController">
<children>
<HBox fx:id="topHBox" prefHeight="22.0" AnchorPane.topAnchor="10.0" AnchorPane.leftAnchor="10.0"
AnchorPane.rightAnchor="10.0">
<children>
<Label text="Filter:" prefWidth="190" id="form-header-text"/>
<Button fx:id="tradeButton">
<graphic>
<ImageView fx:id="tradeButtonImageView"/>
</graphic>
</Button>
<HSpacer prefWidth="10"/>
<TextField fx:id="amount" prefHeight="26.0" prefWidth="60.0" alignment="CENTER_RIGHT">
<HBox.margin>
<Insets left="0.0"/>
</HBox.margin>
</TextField>
<Label text="BTC for">
<padding>
<Insets left="4.0" right="4.0" top="5.0"/>
</padding>
</Label>
<TextField fx:id="price" prefHeight="26.0" prefWidth="70.0" alignment="CENTER_RIGHT"/>
<Label text="EUR/BTC =">
<padding>
<Insets left="4.0" right="4.0" top="5.0"/>
</padding>
</Label>
<TextField fx:id="volume" prefHeight="26.0" prefWidth="70.0" alignment="CENTER_RIGHT" editable="false"/>
<Label prefHeight="21.0" prefWidth="100.0" text="EUR in total">
<padding>
<Insets left="4.0" right="4.0" top="5.0"/>
</padding>
<HBox.margin>
<Insets/>
</HBox.margin>
</Label>
</children>
</HBox>
<VBox fx:id="filterPane" AnchorPane.leftAnchor="10.0" AnchorPane.topAnchor="40.0"
AnchorPane.bottomAnchor="10.0" prefWidth="180.0">
<Separator prefWidth="190"/>
</VBox>
<TableView fx:id="orderBookTable" id="orderbook-table" AnchorPane.leftAnchor="200.0"
AnchorPane.topAnchor="40.0" AnchorPane.bottomAnchor="10.0" AnchorPane.rightAnchor="10.0">
<columns>
<TableColumn text="Amount (Min.)" fx:id="amountColumn" prefWidth="210">
<cellValueFactory>
<PropertyValueFactory property="amount"/>
</cellValueFactory>
</TableColumn>
<TableColumn text="Price" fx:id="priceColumn" prefWidth="160">
<cellValueFactory>
<PropertyValueFactory property="price"/>
</cellValueFactory>
</TableColumn>
<TableColumn text="Volume (Min.)" fx:id="volumeColumn" prefWidth="200">
<cellValueFactory>
<PropertyValueFactory property="volume"/>
</cellValueFactory>
</TableColumn>
</columns>
</TableView>
</children>
</AnchorPane>

View File

@ -0,0 +1,436 @@
package io.bitsquare.gui.trade.tradeprocess;
import com.google.inject.Inject;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.btc.Fees;
import io.bitsquare.gui.IChildController;
import io.bitsquare.gui.INavigationController;
import io.bitsquare.gui.components.VSpacer;
import io.bitsquare.gui.components.processbar.ProcessStepBar;
import io.bitsquare.gui.components.processbar.ProcessStepItem;
import io.bitsquare.gui.util.Colors;
import io.bitsquare.gui.util.Converter;
import io.bitsquare.gui.util.Formatter;
import io.bitsquare.gui.util.Utils;
import io.bitsquare.trade.*;
import io.bitsquare.user.User;
import javafx.animation.AnimationTimer;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.geometry.Insets;
import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import org.controlsfx.dialog.Dialogs;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
public class TradeProcessController implements Initializable, IChildController
{
private TradingFacade tradingFacade;
private Offer offer;
private Trade trade;
private Contract contract;
private INavigationController navigationController;
private List<ProcessStepItem> processStepItems = new ArrayList();
private double requestedAmount;
private VBox vBox;
private TitledPane offerDetailsTitlePane, contractTitlePane;
private ProcessStepBar<String> processStepBar;
private Button nextButton;
private TextField amountTextField;
private Label offererPubKeyLabel, offererAccountPrimaryID, offererAccountSecondaryIDLabel,
offererAccountHolderNameLabel, feedbackLabel, infoLabel, totalLabel, volumeLabel, totalToPayLabel,
totalToReceiveLabel, collateralLabel1, collateralLabel2, amountLabel;
private Pane progressPane;
private ProgressBar progressBar;
private ProgressIndicator progressIndicator;
@FXML
public AnchorPane rootContainer;
@Inject
public TradeProcessController(TradingFacade tradingFacade)
{
this.tradingFacade = tradingFacade;
}
@Override
public void setNavigationController(INavigationController navigationController)
{
this.navigationController = navigationController;
}
@Override
public void initialize(URL url, ResourceBundle rb)
{
}
public void initView(Offer offer, double requestedAmount)
{
this.offer = offer;
this.requestedAmount = requestedAmount;
trade = tradingFacade.createNewTrade(offer);
trade.setRequestedAmount(requestedAmount);
contract = tradingFacade.createNewContract(trade);
processStepItems.add(new ProcessStepItem(takerIsSelling() ? "Sell BTC" : "Buy BTC", Colors.BLUE));
processStepItems.add(new ProcessStepItem("Bank transfer", Colors.BLUE));
processStepItems.add(new ProcessStepItem("Completed", Colors.BLUE));
processStepBar = new ProcessStepBar(processStepItems);
buildStep1();
}
private void trade()
{
double requestedAmount = Converter.convertToDouble(amountTextField.getText());
if (requestedAmount <= offer.getAmount() && requestedAmount >= offer.getMinAmount())
{
amountTextField.setEditable(false);
trade.setRequestedAmount(requestedAmount);
vBox.getChildren().remove(nextButton);
AnchorPane.setTopAnchor(contractTitlePane, 350.0);
progressBar = new ProgressBar();
progressBar.setProgress(0.0);
progressBar.setPrefWidth(200);
progressBar.relocate(10, 10);
progressIndicator = new ProgressIndicator();
progressIndicator.setProgress(-1.0);
progressIndicator.setPrefSize(20, 20);
progressIndicator.relocate(220, 10);
feedbackLabel = new Label();
feedbackLabel.setPadding(new Insets(-10, 0, 0, 0));
feedbackLabel.setId("feedback-text");
feedbackLabel.relocate(10, 50);
progressPane = new Pane();
progressPane.getChildren().addAll(progressBar, progressIndicator, feedbackLabel);
vBox.getChildren().add(progressPane);
sendTakeOfferRequest();
}
else
{
Dialogs.create()
.title("Your input is not valid")
.message("The requested amount you entered is outside of the range of the offered amount.")
.nativeTitleBar()
.lightweight()
.showError();
}
}
// Payment Process
private void sendTakeOfferRequest()
{
tradingFacade.sendTakeOfferRequest(trade);
feedbackLabel.setText("Request take offer confirmation from peer.");
Utils.setTimeout(500, (AnimationTimer animationTimer) -> {
onTakeOfferRequestConfirmed();
progressBar.setProgress(1.0 / 3.0);
return null;
});
}
private void onTakeOfferRequestConfirmed()
{
tradingFacade.payOfferFee(trade);
feedbackLabel.setText("Request offer fee payment confirmation from peer.");
Utils.setTimeout(500, (AnimationTimer animationTimer) -> {
onOfferFeePaymentConfirmed();
progressBar.setProgress(2.0 / 3.0);
return null;
});
}
private void onOfferFeePaymentConfirmed()
{
tradingFacade.requestOffererDetailData();
feedbackLabel.setText("Request detail data from peer.");
Utils.setTimeout(500, (AnimationTimer animationTimer) -> {
onUserDetailsReceived();
progressBar.setProgress(1.0);
return null;
});
}
private void onUserDetailsReceived()
{
tradingFacade.signContract(contract);
tradingFacade.payToDepositTx(trade);
buildWaitBankTransfer();
}
private void buildWaitBankTransfer()
{
processStepBar.next();
vBox.getChildren().remove(progressPane);
vBox.getChildren().remove(offerDetailsTitlePane);
vBox.getChildren().remove(nextButton);
rootContainer.getChildren().remove(contractTitlePane);
infoLabel = new Label("Wait for Bank transfer.");
vBox.getChildren().addAll(infoLabel);
Utils.setTimeout(2000, (AnimationTimer animationTimer) -> {
onBankTransferInited();
return null;
});
}
private void onBankTransferInited()
{
infoLabel.setText("Bank transfer has been inited.\nCheck your bank account and continue when you have received the money.\n");
nextButton.setText("Money received on Bank account");
nextButton.setOnAction(e -> releaseBTC());
vBox.getChildren().add(nextButton);
}
private void releaseBTC()
{
processStepBar.next();
tradingFacade.releaseBTC(trade);
vBox.getChildren().remove(infoLabel);
nextButton.setText("Close");
nextButton.setOnAction(e -> close());
GridPane summaryGridPane = new GridPane();
int row = 0;
summaryGridPane.setVgap(5);
summaryGridPane.setHgap(5);
summaryGridPane.setPadding(new Insets(5, 5, 5, 5));
addLabel(summaryGridPane, "You have payed:", getTotalToPay(), ++row);
addLabel(summaryGridPane, "You have received:\n ", getTotalToReceive(), ++row);
TitledPane summaryTitlePane = new TitledPane("Trade summary:", summaryGridPane);
summaryTitlePane.setCollapsible(false);
vBox.getChildren().add(2, summaryTitlePane);
}
private void close()
{
TabPane tabPane = ((TabPane) (rootContainer.getParent().getParent()));
tabPane.getTabs().remove(tabPane.getSelectionModel().getSelectedItem());
navigationController.navigateToView(INavigationController.TRADE__ORDER_BOOK, "Orderbook");
}
private void buildStep1()
{
OfferConstraints offerConstraints = offer.getOfferConstraints();
User taker = contract.getTaker();
User offerer = contract.getOfferer();
GridPane offerDetailsGridPane = new GridPane();
int row = 0;
offerDetailsGridPane.setVgap(5);
offerDetailsGridPane.setHgap(5);
offerDetailsGridPane.setPadding(new Insets(5, 5, 5, 5));
amountTextField = addInputField(offerDetailsGridPane, "Amount (BTC):", Formatter.formatAmount(getAmount()), ++row);
amountTextField.textProperty().addListener(e -> {
setTotal();
setVolume();
setCollateral();
totalToPayLabel.setText(getTotalToPay());
totalToReceiveLabel.setText(getTotalToReceive());
amountLabel.setText(amountTextField.getText());
});
offerDetailsGridPane.add(new Label("(" + offer.getAmount() + "BTC - " + offer.getMinAmount() + "BTC)"), 2, row);
addLabel(offerDetailsGridPane, "Price:", Formatter.formatPriceWithCurrencyPair(offer.getPrice(), offer.getCurrency()), ++row);
totalLabel = addLabel(offerDetailsGridPane, "Total:", "", ++row);
setTotal();
collateralLabel1 = addLabel(offerDetailsGridPane, "Collateral:", Formatter.formatCollateral(offer.getOfferConstraints().getCollateral(), getAmount()), ++row);
addLabel(offerDetailsGridPane, "Offer fee:", Formatter.formatSatoshis(Fees.OFFER_CREATION_FEE, true), ++row);
addVSpacer(offerDetailsGridPane, ++row);
totalToPayLabel = addLabel(offerDetailsGridPane, "You pay:", getTotalToPay(), ++row);
totalToReceiveLabel = addLabel(offerDetailsGridPane, "You receive:\n ", getTotalToReceive(), ++row);
offerDetailsTitlePane = new TitledPane(takerIsSelling() ? "Sell Bitcoin" : "Buy Bitcoin", offerDetailsGridPane);
offerDetailsTitlePane.setCollapsible(false);
nextButton = new Button(processStepItems.get(0).getLabel());
nextButton.setDefaultButton(true);
nextButton.setOnAction(e -> trade());
GridPane contractGridPane = new GridPane();
contractGridPane.setVgap(5);
contractGridPane.setHgap(5);
contractGridPane.setPadding(new Insets(5, 5, 5, 5));
row = 0;
addHeaderLabel(contractGridPane, "Offer details:", row);
addLabel(contractGridPane, "Offer ID:", offer.getUid().toString(), ++row);
addLabel(contractGridPane, "Offer type:", Formatter.formatDirection((offer.getDirection() == Direction.BUY ? Direction.SELL : Direction.BUY), false), ++row);
amountLabel = addLabel(contractGridPane, "Amount:", Formatter.formatAmount(getAmount()), ++row);
volumeLabel = addLabel(contractGridPane, "Volume:", "", ++row);
setVolume();
addLabel(contractGridPane, "Price:", Formatter.formatPriceWithCurrencyPair(offer.getPrice(), offer.getCurrency()), ++row);
collateralLabel2 = addLabel(contractGridPane, "Collateral:", "", ++row);
setCollateral();
addLabel(contractGridPane, "Language:", Formatter.formatList(offerConstraints.getLanguages()), ++row);
addLabel(contractGridPane, "Arbitrator:", offerConstraints.getArbitrator(), ++row);
// addLabel(contractGridPane, "Identity verification:", Formatter.formatList(offerConstraints.getIdentityVerifications()), ++row);
addLabel(contractGridPane, "Bank transfer reference ID:", "Purchase xyz 01.04.2014", ++row);
addVSpacer(contractGridPane, ++row);
addHeaderLabel(contractGridPane, "Offerer data:", ++row);
addLabel(contractGridPane, "Account ID:", offerer.getAccountID(), ++row);
addLabel(contractGridPane, "Messaging ID:", offerer.getMessageID(), ++row);
addLabel(contractGridPane, "Country:", offerer.getCountry(), ++row);
offererPubKeyLabel = addLabel(contractGridPane, "Payment public key:", contract.getOffererPubKey(), ++row);
addLabel(contractGridPane, "Bank transfer type:", offerer.getBankDetails().getBankTransferType(), ++row);
offererAccountPrimaryID = addLabel(contractGridPane, "Bank account IBAN:", offerer.getBankDetails().getAccountPrimaryID(), ++row);
offererAccountSecondaryIDLabel = addLabel(contractGridPane, "Bank account BIC:", offerer.getBankDetails().getAccountSecondaryID(), ++row);
offererAccountHolderNameLabel = addLabel(contractGridPane, "Bank account holder:", offerer.getBankDetails().getAccountHolderName(), ++row);
addVSpacer(contractGridPane, ++row);
addHeaderLabel(contractGridPane, "Offer taker data:", ++row);
addLabel(contractGridPane, "Account ID:", taker.getAccountID(), ++row);
addLabel(contractGridPane, "Messaging ID:", taker.getMessageID(), ++row);
addLabel(contractGridPane, "Country:", taker.getCountry(), ++row);
addLabel(contractGridPane, "Payment public key:", contract.getTakerPubKey(), ++row);
addLabel(contractGridPane, "Bank transfer type:", taker.getBankDetails().getBankTransferType(), ++row);
addLabel(contractGridPane, "Bank account IBAN:", taker.getBankDetails().getAccountPrimaryID(), ++row);
addLabel(contractGridPane, "Bank account BIC:", taker.getBankDetails().getAccountSecondaryID(), ++row);
addLabel(contractGridPane, "Bank account holder:", taker.getBankDetails().getAccountHolderName(), ++row);
ScrollPane scrollPane = new ScrollPane();
scrollPane.setContent(contractGridPane);
contractTitlePane = new TitledPane("Contract", scrollPane);
contractTitlePane.setCollapsible(false);
AnchorPane.setLeftAnchor(contractTitlePane, 10.0);
AnchorPane.setRightAnchor(contractTitlePane, 10.0);
AnchorPane.setTopAnchor(contractTitlePane, 324.0);
AnchorPane.setBottomAnchor(contractTitlePane, 10.0);
vBox = new VBox();
AnchorPane.setLeftAnchor(vBox, 10.0);
AnchorPane.setRightAnchor(vBox, 10.0);
AnchorPane.setTopAnchor(vBox, 10.0);
vBox.setSpacing(10);
vBox.getChildren().addAll(processStepBar, new VSpacer(5), offerDetailsTitlePane, nextButton);
rootContainer.getChildren().addAll(vBox, contractTitlePane);
}
private Label addLabel(GridPane gridPane, String title, String value, int row)
{
gridPane.add(new Label(title), 0, row);
Label valueLabel = new Label(value);
gridPane.add(valueLabel, 1, row);
return valueLabel;
}
private void addHeaderLabel(GridPane gridPane, String title, int row)
{
Label headerLabel = new Label(title);
headerLabel.setId("form-header-text");
gridPane.add(headerLabel, 0, row);
}
private TextField addInputField(GridPane gridPane, String title, String value, int row)
{
gridPane.add(new Label(title), 0, row);
TextField textField = new TextField(value);
gridPane.add(textField, 1, row);
return textField;
}
private void addVSpacer(GridPane gridPane, int row)
{
gridPane.add(new VSpacer(10), 0, row);
}
private void setTotal()
{
totalLabel.setText(Formatter.formatVolume(getVolume()));
}
private void setVolume()
{
totalLabel.setText(Formatter.formatVolume(getVolume(), offer.getCurrency()));
}
private boolean takerIsSelling()
{
return offer.getDirection() == Direction.BUY;
}
private double getVolume()
{
return offer.getPrice() * Converter.convertToDouble(amountTextField.getText());
}
private double getAmount()
{
return requestedAmount > 0 ? requestedAmount : offer.getAmount();
}
private String getTotalToPay()
{
String result = "";
if (takerIsSelling())
{
double btcValue = Converter.convertToDouble(amountTextField.getText()) + BtcFormatter.satoshiToBTC(Fees.OFFER_CREATION_FEE) +
offer.getOfferConstraints().getCollateral() * Converter.convertToDouble(amountTextField.getText());
result = Formatter.formatAmount(btcValue, true, true);
}
else
{
double btcValue = BtcFormatter.satoshiToBTC(Fees.OFFER_CREATION_FEE) + offer.getOfferConstraints().getCollateral() * Converter.convertToDouble(amountTextField.getText());
result = Formatter.formatAmount(btcValue, true, true) + "\n" + Formatter.formatVolume(getVolume(), offer.getCurrency());
}
return result;
}
private String getTotalToReceive()
{
String result = "";
if (takerIsSelling())
{
double btcValue = offer.getOfferConstraints().getCollateral() * Converter.convertToDouble(amountTextField.getText());
result = Formatter.formatAmount(btcValue, true, true) + "\n" + Formatter.formatVolume(getVolume(), offer.getCurrency());
}
else
{
double btcValue = Converter.convertToDouble(amountTextField.getText()) +
offer.getOfferConstraints().getCollateral() * Converter.convertToDouble(amountTextField.getText());
result = Formatter.formatAmount(btcValue, true, true);
}
return result;
}
public void setCollateral()
{
String value = Formatter.formatCollateral(offer.getOfferConstraints().getCollateral(), Converter.convertToDouble(amountTextField.getText()));
collateralLabel1.setText(value);
collateralLabel2.setText(value);
}
}

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:id="rootContainer"
AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0"
AnchorPane.topAnchor="0.0"
AnchorPane.bottomAnchor="0.0"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="io.bitsquare.gui.trade.tradeprocess.TradeProcessController"/>

View File

@ -0,0 +1,13 @@
package io.bitsquare.gui.util;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
public class Colors
{
public static final Paint YELLOW = Color.valueOf("#edc035");
public static final Paint BLUE = Color.valueOf("#0096c9");
public static final Paint LIGHT_GREY = Color.valueOf("#f4f4f4");
public static final Paint DARK_GREY = Color.valueOf("#333333");
}

View File

@ -0,0 +1,24 @@
package io.bitsquare.gui.util;
import io.bitsquare.settings.Settings;
import java.text.DecimalFormat;
import java.text.ParseException;
public class Converter
{
public static double convertToDouble(String input)
{
try
{
DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance(Settings.getLocale());
return decimalFormat.parse(input).doubleValue();
} catch (ParseException e)
{
//e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
return 0.0;
}
}

View File

@ -0,0 +1,126 @@
package io.bitsquare.gui.util;
import io.bitsquare.btc.BtcFormatter;
import io.bitsquare.settings.Settings;
import io.bitsquare.trade.Direction;
import java.math.BigInteger;
import java.text.DecimalFormat;
import java.util.Currency;
import java.util.List;
public class Formatter
{
public static String formatPrice(double price)
{
return formatDouble(price);
}
public static String formatPriceWithCurrencyPair(double price, Currency currency)
{
return formatDouble(price) + " " + currency.toString() + "/BTC";
}
public static String formatAmount(double amount, boolean useBTC, boolean exact)
{
return formatDouble(amount, (exact ? 4 : 2)) + (useBTC ? " BTC" : "");
}
public static String formatAmount(double amount, boolean useBTC)
{
return formatAmount(amount, useBTC, false);
}
public static String formatAmount(double amount)
{
return formatAmount(amount, false);
}
public static String formatAmountWithMinAmount(double amount, double minAmount, boolean useBTC)
{
if (useBTC)
return formatDouble(amount) + " BTC (" + formatDouble(minAmount) + " BTC)";
else
return formatDouble(amount) + " (" + formatDouble(minAmount) + ")";
}
public static String formatAmountWithMinAmount(double amount, double minAmount)
{
return formatAmountWithMinAmount(amount, minAmount, false);
}
public static String formatVolume(double volume)
{
return formatDouble(volume);
}
public static String formatVolume(double volume, Currency currency)
{
return formatDouble(volume) + " " + currency.toString();
}
public static String formatVolumeWithMinVolume(double volume, double minVolume, Currency currency)
{
return formatDouble(volume) + " " + currency.toString() + " (" + formatDouble(minVolume) + " " + currency.toString() + ")";
}
public static String formatVolumeWithMinVolume(double volume, double minVolume)
{
return formatDouble(volume) + " (" + formatDouble(minVolume) + ")";
}
public static String formatCollateral(double collateral, double amount)
{
return formatPercent(collateral) + " (" + formatDouble(collateral * amount, 4) + " BTC)";
}
public static String formatDirection(Direction direction, boolean allUpperCase)
{
String result = (direction == Direction.BUY) ? "Buy" : "Sell";
if (allUpperCase)
result = result.toUpperCase();
return result;
}
public static String formatList(List<String> list)
{
String s = list.toString();
return s.substring(1, s.length() - 1);
}
public static String formatSatoshis(BigInteger satoshis, boolean useBTC)
{
if (useBTC)
return formatDouble(BtcFormatter.satoshiToBTC(satoshis), 4) + " BTC";
else
return formatDouble(BtcFormatter.satoshiToBTC(satoshis), 4);
}
public static String formatDouble(double value)
{
return formatDouble(value, 2);
}
public static String formatDouble(double value, int fractionDigits)
{
DecimalFormat decimalFormat = getDecimalFormat(fractionDigits);
return decimalFormat.format(value);
}
public static DecimalFormat getDecimalFormat(int fractionDigits)
{
DecimalFormat decimalFormat = (DecimalFormat) DecimalFormat.getInstance(Settings.getLocale());
decimalFormat.setMinimumFractionDigits(fractionDigits);
decimalFormat.setMaximumFractionDigits(fractionDigits);
decimalFormat.setGroupingUsed(false);
return decimalFormat;
}
private static String formatPercent(double value)
{
return value * 100 + "%";
}
}

View File

@ -0,0 +1,33 @@
package io.bitsquare.gui.util;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class Icons
{
public static final String HOME = "/images/home.png";
public static final String NAV_BUY = "/images/nav_buy.png";
public static final String NAV_BUY_ACTIVE = "/images/nav_buy_active.png";
public static final String NAV_SELL = "/images/nav_sell.png";
public static final String NAV_SELL_ACTIVE = "/images/nav_sell_active.png";
public static final String ORDERS = "/images/orders.png";
public static final String HISTORY = "/images/history.png";
public static final String FUNDS = "/images/funds.png";
public static final String MSG = "/images/msg.png";
public static final String SETTINGS = "/images/settings.png";
public static final String BUY = "/images/buy.png";
public static final String SELL = "/images/sell.png";
public static final String REMOVE = "/images/remove_minus_9.png";
public static final String ADD = "/images/list.png";
public static Image getIconImage(String iconName)
{
return new Image(Icons.class.getResourceAsStream(iconName));
}
public static ImageView getIconImageView(String iconName)
{
return new ImageView(new Image(Icons.class.getResourceAsStream(iconName)));
}
}

View File

@ -0,0 +1,71 @@
package io.bitsquare.gui.util;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
public class Loc
{
public static String get(String key)
{
ResourceBundle bundle = ResourceBundle.getBundle("i18n.displayStrings", new UTF8Control());
return bundle.getString(key);
}
public static String get(String key, String... arguments)
{
String entry = ResourceBundle.getBundle("i18n.displayStrings", new UTF8Control()).getString(key);
return MessageFormat.format(entry, arguments);
}
}
class UTF8Control extends ResourceBundle.Control
{
public ResourceBundle newBundle
(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException
{
// The below is a copy of the default implementation.
String bundleName = toBundleName(baseName, locale);
String resourceName = toResourceName(bundleName, "properties");
ResourceBundle bundle = null;
InputStream stream = null;
if (reload)
{
URL url = loader.getResource(resourceName);
if (url != null)
{
URLConnection connection = url.openConnection();
if (connection != null)
{
connection.setUseCaches(false);
stream = connection.getInputStream();
}
}
}
else
{
stream = loader.getResourceAsStream(resourceName);
}
if (stream != null)
{
try
{
// Only this line is changed to make it to read properties files as UTF-8.
bundle = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8"));
} finally
{
stream.close();
}
}
return bundle;
}
}

View File

@ -0,0 +1,35 @@
package io.bitsquare.gui.util;
import javafx.animation.AnimationTimer;
import java.util.function.Function;
public class Utils
{
/**
* @param delay in milliseconds
* @param callback
* @usage Utils.setTimeout(1000, (AnimationTimer animationTimer) -> {
* doSomething();
* return null;
* });
*/
public static void setTimeout(int delay, Function<AnimationTimer, Void> callback)
{
long startTime = System.currentTimeMillis();
AnimationTimer animationTimer = new AnimationTimer()
{
@Override
public void handle(long arg0)
{
if (System.currentTimeMillis() > delay + startTime)
{
callback.apply(this);
this.stop();
}
}
};
animationTimer.start();
}
}

View File

@ -0,0 +1,13 @@
package io.bitsquare.msg;
/**
* Gateway to messaging
*/
public interface IMessageFacade
{
void broadcast(Message message);
void send(Message message, String receiverMsgID);
void registerListener(String listenerPubKey);
}

View File

@ -0,0 +1,60 @@
package io.bitsquare.msg;
import io.bitsquare.trade.Contract;
import io.bitsquare.trade.Offer;
import io.bitsquare.trade.Trade;
import io.bitsquare.util.Utils;
public class Message
{
public final static String BROADCAST_NEW_OFFER = "BROADCAST_NEW_OFFER";
public final static String REQUEST_TAKE_OFFER = "REQUEST_TAKE_OFFER";
public final static String OFFER_ACCEPTED = "OFFER_ACCEPTED";
public final static String REQUEST_OFFER_FEE_PAYMENT_CONFIRM = "REQUEST_OFFER_FEE_PAYMENT_CONFIRM";
public final static String SEND_SIGNED_CONTRACT = "SEND_SIGNED_CONTRACT";
private String type;
private Object payload;
public Message(String type, String msg)
{
this.type = type;
this.payload = msg;
}
public Message(String type, Trade trade)
{
this.type = type;
this.payload = trade;
}
public Message(String type, Offer offer)
{
this.type = type;
this.payload = offer;
}
public Message(String type, Contract contract)
{
this.type = type;
this.payload = contract;
}
public String toString()
{
return type + ": " + Utils.convertToJson(payload);
}
public String getType()
{
return type;
}
public void setType(String type)
{
this.type = type;
}
}

View File

@ -0,0 +1,28 @@
package io.bitsquare.msg;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MessageFacade implements IMessageFacade
{
private static final Logger log = LoggerFactory.getLogger(MessageFacade.class);
@Override
public void broadcast(Message message)
{
log.info(message.toString());
}
@Override
public void send(Message message, String receiverPubKey)
{
log.info(message.toString() + "/" + receiverPubKey);
}
@Override
public void registerListener(String listenerPubKey)
{
log.info(listenerPubKey);
}
}

View File

@ -0,0 +1,89 @@
package io.bitsquare.settings;
import com.google.inject.Inject;
import io.bitsquare.storage.IStorage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
public class OrderBookFilterSettings
{
private static final Logger log = LoggerFactory.getLogger(OrderBookFilterSettings.class);
private IStorage storage;
private Currency currency;
private ArrayList<Currency> currencies;
@Inject
public OrderBookFilterSettings(IStorage storage)
{
this.storage = storage;
currencies = getCurrencies();
currency = (Currency) storage.read("OrderBookFilterSettings.currency");
if (currency == null)
setCurrency(currencies.get(0));
}
public enum BankTransferTypes
{
SEPA, OKPAY, WIRE, PERFECT_MONEY, OTHER, ANY
}
public enum Arbitrators
{
PAYSTY, TLS_NOTARY, BIT_RATED, OTHER, ANY, NONE
}
public enum IdVerifications
{
PGP, BTC_OTC, OPEN_ID, NAME_COIN, NAME_ID, PASSPORT, SKYPE, FACEBOOK, GOOGLE_PLUS, TWITTER, OTHER, ANY, NONE
}
//TODO remove duplicated entries, insert separators
public ArrayList<Currency> getCurrencies()
{
ArrayList<Currency> currencies = new ArrayList<>();
currencies.add(Currency.getInstance("USD"));
currencies.add(Currency.getInstance("EUR"));
currencies.add(Currency.getInstance("CNY"));
currencies.add(Currency.getInstance("RUB"));
currencies.add(Currency.getInstance("JPY"));
currencies.add(Currency.getInstance("GBP"));
currencies.add(Currency.getInstance("CAD"));
currencies.add(Currency.getInstance("AUD"));
currencies.add(Currency.getInstance("CHF"));
currencies.add(Currency.getInstance("CNY"));
Set<Currency> otherCurrenciesSet = Currency.getAvailableCurrencies();
ArrayList<Currency> otherCurrenciesList = new ArrayList<>();
otherCurrenciesList.addAll(otherCurrenciesSet);
Collections.sort(otherCurrenciesList, new CurrencyComparator());
currencies.addAll(otherCurrenciesList);
return currencies;
}
public Currency getCurrency()
{
return currency;
}
public void setCurrency(Currency currency)
{
this.currency = currency;
storage.write("OrderBookFilterSettings.currency", currency);
}
}
class CurrencyComparator implements Comparator<Currency>
{
@Override
public int compare(Currency a, Currency b)
{
return a.getCurrencyCode().compareTo(b.getCurrencyCode());
}
}

View File

@ -0,0 +1,187 @@
package io.bitsquare.settings;
import com.google.inject.Inject;
import io.bitsquare.storage.IStorage;
import io.bitsquare.trade.orderbook.OrderBookFilter;
import java.util.ArrayList;
import java.util.Currency;
import java.util.Locale;
public class Settings
{
public static Locale locale = Locale.ENGLISH;
public static Currency currency = Currency.getInstance("USD");
private IStorage storage;
private OrderBookFilter orderBookFilter;
public static Locale getLocale()
{
return Settings.locale;
}
public static Currency getCurrency()
{
return Settings.currency;
}
@Inject
public Settings(IStorage storage, OrderBookFilter orderBookFilter)
{
this.orderBookFilter = orderBookFilter;
locale = Locale.ENGLISH;
currency = Currency.getInstance("USD");
this.storage = storage;
currency = (Currency) storage.read("Settings.currency");
if (currency == null)
currency = Currency.getInstance("USD");
}
//TODO remove duplicated entries, insert separators
public ArrayList<Currency> getAllCurrencies()
{
ArrayList<Currency> currencies = new ArrayList<>();
currencies.add(Currency.getInstance("USD"));
currencies.add(Currency.getInstance("EUR"));
currencies.add(Currency.getInstance("CNY"));
currencies.add(Currency.getInstance("RUB"));
currencies.add(Currency.getInstance("JPY"));
currencies.add(Currency.getInstance("GBP"));
currencies.add(Currency.getInstance("CAD"));
currencies.add(Currency.getInstance("AUD"));
currencies.add(Currency.getInstance("CHF"));
currencies.add(Currency.getInstance("CNY"));
/* Set<Currency> otherCurrenciesSet = Currency.getAvailableCurrencies();
ArrayList<Currency> otherCurrenciesList = new ArrayList<>();
otherCurrenciesList.addAll(otherCurrenciesSet);
Collections.sort(otherCurrenciesList, new CurrencyComparator());
currencies.addAll(otherCurrenciesList); */
return currencies;
}
public ArrayList<String> getAllBankTransferTypes()
{
ArrayList<String> bankTransferTypes = new ArrayList<>();
bankTransferTypes.add("SEPA");
bankTransferTypes.add("Wire");
bankTransferTypes.add("International");
bankTransferTypes.add("OKPay");
bankTransferTypes.add("Netteller");
bankTransferTypes.add("Perfect Money");
bankTransferTypes.add("Any");
return bankTransferTypes;
}
public ArrayList<String> getAllCountries()
{
ArrayList<String> bankTransferTypes = new ArrayList<>();
bankTransferTypes.add("USA");
bankTransferTypes.add("GB");
bankTransferTypes.add("DE");
bankTransferTypes.add("FR");
bankTransferTypes.add("ES");
bankTransferTypes.add("CH");
bankTransferTypes.add("RUS");
bankTransferTypes.add("AUS");
bankTransferTypes.add("CAN");
bankTransferTypes.add("AT");
return bankTransferTypes;
}
/*
public ArrayList<String> getAllCountries()
{
ArrayList<String> result = new ArrayList<>();
for (Locale locale : Locale.getAvailableLocales())
{
result.add(locale.getDisplayCountry());
}
return result;
} */
/*public ArrayList<String> getAllLanguages()
{
ArrayList<String> result = new ArrayList<>();
for (Locale locale : Locale.getAvailableLocales())
{
result.add(locale.getDisplayLanguage());
}
return result;
} */
public ArrayList<String> getAllLanguages()
{
ArrayList<String> bankTransferTypes = new ArrayList<>();
bankTransferTypes.add("English");
bankTransferTypes.add("Chinese");
bankTransferTypes.add("Spanish");
bankTransferTypes.add("Russian");
bankTransferTypes.add("French");
bankTransferTypes.add("Italian");
return bankTransferTypes;
}
public ArrayList<String> getAllArbitrators()
{
ArrayList<String> arbitrators = new ArrayList<>();
arbitrators.add("Paysty pool 1");
arbitrators.add("Paysty pool 2");
arbitrators.add("Paysty pool 3");
arbitrators.add("Paysty pool 4");
return arbitrators;
}
public ArrayList<String> getAllIdentityVerifications()
{
ArrayList<String> identityVerifications = new ArrayList<>();
identityVerifications.add("Passport");
identityVerifications.add("PGP");
identityVerifications.add("BTC-OTC");
identityVerifications.add("Bitcointalk");
identityVerifications.add("Reddit");
identityVerifications.add("Skype");
identityVerifications.add("Google+");
identityVerifications.add("Twitter");
identityVerifications.add("Diaspora");
identityVerifications.add("Facebook");
identityVerifications.add("Jabber");
identityVerifications.add("Other");
identityVerifications.add("Any");
identityVerifications.add("None");
return identityVerifications;
}
public ArrayList<String> getAllCollaterals()
{
ArrayList<String> list = new ArrayList<>();
list.add("0.01");
list.add("0.1");
list.add("0.5");
list.add("1.0");
return list;
}
public void setCurrency(Currency currency)
{
Settings.currency = currency;
storage.write("Settings.currency", currency);
}
public void setLocale(Locale locale)
{
Settings.locale = locale;
}
public OrderBookFilter getOrderBookFilter()
{
return orderBookFilter;
}
}

View File

@ -0,0 +1,6 @@
package io.bitsquare.setup;
public interface ISetup
{
void applyPersistedData();
}

View File

@ -0,0 +1,59 @@
package io.bitsquare.setup;
import com.google.inject.Inject;
import io.bitsquare.settings.Settings;
import io.bitsquare.storage.IStorage;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.orderbook.OrderBookFilter;
import io.bitsquare.user.BankDetails;
import io.bitsquare.user.User;
import java.util.UUID;
public class MockSetup implements ISetup
{
private IStorage storage;
private User user;
private OrderBookFilter orderBookFilter;
@Inject
public MockSetup(IStorage storage, User user, OrderBookFilter orderBookFilter)
{
this.storage = storage;
this.user = user;
this.orderBookFilter = orderBookFilter;
}
@Override
public void applyPersistedData()
{
String accountID = (String) storage.read("User.accountID");
if (accountID == null)
{
storage.write("User.accountID", UUID.randomUUID().toString());
storage.write("User.messageID", UUID.randomUUID().toString());
storage.write("User.country", "ES");
storage.write("BankDetails.bankTransferType", "SEPA");
storage.write("BankDetails.accountPrimaryID", "IBAN_12312");
storage.write("BankDetails.accountSecondaryID", "BIC_123123");
storage.write("BankDetails.accountHolderName", "Bob Brown");
}
user.setAccountID((String) storage.read("User.accountID"));
user.setMessageID((String) storage.read("User.messageID"));
user.setCountry((String) storage.read("User.country"));
user.setBankDetails(new BankDetails((String) storage.read("BankDetails.bankTransferType"),
(String) storage.read("BankDetails.accountPrimaryID"),
(String) storage.read("BankDetails.accountSecondaryID"),
(String) storage.read("BankDetails.accountHolderName")));
// todo use persistence
orderBookFilter.setAmount(0.0);
orderBookFilter.setPrice(0.0);
orderBookFilter.setDirection(Direction.BUY);
orderBookFilter.setCurrency(Settings.getCurrency());
}
}

View File

@ -0,0 +1,8 @@
package io.bitsquare.storage;
public interface IStorage
{
void write(String key, Object value);
Object read(String key);
}

View File

@ -0,0 +1,95 @@
package io.bitsquare.storage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
/**
* Simple storage solution for serialized data
*/
public class SimpleStorage implements IStorage
{
private static final Logger log = LoggerFactory.getLogger(SimpleStorage.class);
private final String preferencesFileName = "preferences.ser";
private final String storageFile;
private DataVO dataVO;
public SimpleStorage()
{
storageFile = SimpleStorage.class.getProtectionDomain().getCodeSource().getLocation().getFile() + "/" + preferencesFileName;
dataVO = readDataVO();
if (dataVO == null)
{
dataVO = new DataVO();
dataVO.dict = new HashMap<String, Object>();
writeDataVO(dataVO);
}
}
@Override
public void write(String key, Object value)
{
log.info("Write object with key = " + key + " / value = " + value);
dataVO.dict.put(key, value);
writeDataVO(dataVO);
}
@Override
public Object read(String key)
{
dataVO = readDataVO();
Object result = dataVO.dict.get(key);
log.info("Read object with key = " + key + " result = " + result);
return result;
}
private void writeDataVO(DataVO dataVO)
{
try
{
FileOutputStream fileOut = new FileOutputStream(storageFile);
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(dataVO);
out.close();
fileOut.close();
} catch (IOException i)
{
i.printStackTrace();
}
}
private DataVO readDataVO()
{
DataVO dataVO = null;
File file = new File(storageFile);
if (file.exists())
{
try
{
FileInputStream fileIn = new FileInputStream(file);
ObjectInputStream in = new ObjectInputStream(fileIn);
dataVO = (DataVO) in.readObject();
in.close();
fileIn.close();
} catch (IOException i)
{
i.printStackTrace();
} catch (ClassNotFoundException c)
{
c.printStackTrace();
}
}
return dataVO;
}
}
class DataVO implements Serializable
{
public Map<String, Object> dict;
}

View File

@ -0,0 +1,78 @@
package io.bitsquare.trade;
import io.bitsquare.user.User;
import java.util.UUID;
public class Contract
{
private User taker;
private User offerer;
private String offererPubKey;
private Trade trade;
private String takerPubKey;
public Contract(Trade trade, String takerPubKey)
{
this.trade = trade;
this.takerPubKey = takerPubKey;
}
public UUID getUid()
{
return trade.getUid();
}
public void setTaker(User taker)
{
this.taker = taker;
}
public User getTaker()
{
return taker;
}
public User getOfferer()
{
return offerer;
}
public void setOfferer(User offerer)
{
this.offerer = offerer;
}
public String getTakerPubKey()
{
return takerPubKey;
}
public void setTakerPubKey(String takerPubKey)
{
this.takerPubKey = takerPubKey;
}
public String getOffererPubKey()
{
return offererPubKey;
}
public void setOffererPubKey(String offererPubKey)
{
this.offererPubKey = offererPubKey;
}
public Trade getTrade()
{
return trade;
}
public void setTrade(Trade trade)
{
this.trade = trade;
}
}

View File

@ -0,0 +1,6 @@
package io.bitsquare.trade;
public enum Direction
{
BUY, SELL
}

View File

@ -0,0 +1,129 @@
package io.bitsquare.trade;
import io.bitsquare.user.User;
import java.util.Currency;
import java.util.UUID;
public class Offer
{
private UUID uid;
private double price;
private double amount;
private double minAmount;
private Direction direction;
private Currency currency;
private User offerer;
private OfferConstraints offerConstraints;
public Offer(UUID uid,
Direction direction,
double price,
double amount,
double minAmount,
Currency currency,
User offerer,
OfferConstraints offerConstraints)
{
this.uid = uid;
this.direction = direction;
this.price = price;
this.amount = amount;
this.minAmount = minAmount;
this.currency = currency;
this.offerer = offerer;
this.offerConstraints = offerConstraints;
}
public double getVolume()
{
return price * amount;
}
public double getMinVolume()
{
return price * minAmount;
}
public UUID getUid()
{
return uid;
}
public void setUid(UUID uid)
{
this.uid = uid;
}
public Direction getDirection()
{
return direction;
}
public void setDirection(Direction direction)
{
this.direction = direction;
}
public double getPrice()
{
return price;
}
public void setPrice(double price)
{
this.price = price;
}
public double getAmount()
{
return amount;
}
public void setAmount(double amount)
{
this.amount = amount;
}
public double getMinAmount()
{
return minAmount;
}
public void setMinAmount(double minAmount)
{
this.minAmount = minAmount;
}
public Currency getCurrency()
{
return currency;
}
public void setCurrency(Currency currency)
{
this.currency = currency;
}
public OfferConstraints getOfferConstraints()
{
return offerConstraints;
}
public void setOfferConstraints(OfferConstraints offerConstraints)
{
this.offerConstraints = offerConstraints;
}
public User getOfferer()
{
return offerer;
}
public void setOfferer(User offerer)
{
this.offerer = offerer;
}
}

View File

@ -0,0 +1,58 @@
package io.bitsquare.trade;
import java.util.List;
public class OfferConstraints
{
private double collateral;
private List<String> countries;
private List<String> languages;
private List<String> bankTransferTypes;
private String arbitrator;
private String identityVerification;
public OfferConstraints(List<String> countries,
List<String> languages,
double collateral,
List<String> bankTransferTypes,
String arbitrator,
String identityVerification)
{
this.countries = countries;
this.languages = languages;
this.collateral = collateral;
this.bankTransferTypes = bankTransferTypes;
this.arbitrator = arbitrator;
this.identityVerification = identityVerification;
}
public double getCollateral()
{
return collateral;
}
public List<String> getCountries()
{
return countries;
}
public List<String> getLanguages()
{
return languages;
}
public List<String> getBankTransferTypes()
{
return bankTransferTypes;
}
public String getArbitrator()
{
return arbitrator;
}
public String getIdentityVerification()
{
return identityVerification;
}
}

View File

@ -0,0 +1,86 @@
package io.bitsquare.trade;
import java.util.UUID;
public class Trade
{
private Offer offer;
private boolean takeOfferRequested;
private boolean takeOfferAccepted;
private double requestedAmount;
private boolean takeOfferFeePayed;
private boolean takeOfferFeePaymentConfirmed;
private String jsonRepresentation;
private String signature;
private String takeOfferFeeTxID;
public Trade(Offer offer)
{
this.offer = offer;
}
public Offer getOffer()
{
return offer;
}
public UUID getUid()
{
return offer.getUid();
}
public void setJsonRepresentation(String jsonRepresentation)
{
this.jsonRepresentation = jsonRepresentation;
}
public void setSignature(String signature)
{
this.signature = signature;
}
public boolean isTakeOfferRequested()
{
return takeOfferRequested;
}
public void setTakeOfferRequested(boolean takeOfferRequested)
{
this.takeOfferRequested = takeOfferRequested;
}
public boolean isTakeOfferAccepted()
{
return takeOfferAccepted;
}
public void setTakeOfferAccepted(boolean takeOfferAccepted)
{
this.takeOfferAccepted = takeOfferAccepted;
}
public double getRequestedAmount()
{
return requestedAmount;
}
public void setRequestedAmount(double requestedAmount)
{
this.requestedAmount = requestedAmount;
}
public void setTakeOfferFeePayed(boolean takeOfferFeePayed)
{
this.takeOfferFeePayed = takeOfferFeePayed;
}
public void setTakeOfferFeePaymentConfirmed(boolean takeOfferFeePaymentConfirmed)
{
this.takeOfferFeePaymentConfirmed = takeOfferFeePaymentConfirmed;
}
public void setTakeOfferFeeTxID(String takeOfferFeeTxID)
{
this.takeOfferFeeTxID = takeOfferFeeTxID;
}
}

View File

@ -0,0 +1,177 @@
package io.bitsquare.trade;
import com.google.inject.Inject;
import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.KeyPair;
import io.bitsquare.btc.MockWalletFacade;
import io.bitsquare.crypto.ICryptoFacade;
import io.bitsquare.msg.IMessageFacade;
import io.bitsquare.msg.Message;
import io.bitsquare.settings.Settings;
import io.bitsquare.user.User;
import io.bitsquare.util.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.UUID;
/**
* Main facade for operating with trade domain between GUI and services (msg, btc)
*/
public class TradingFacade
{
private static final Logger log = LoggerFactory.getLogger(TradingFacade.class);
private final HashMap<String, Offer> offers = new HashMap<>();
private final HashMap<String, Trade> trades = new HashMap<>();
private final HashMap<String, Contract> contracts = new HashMap<>();
private User user;
private IMessageFacade messageFacade;
private BlockChainFacade blockChainFacade;
private MockWalletFacade walletFacade;
private ICryptoFacade cryptoFacade;
private Settings settings;
@Inject
public TradingFacade(User user,
Settings settings,
IMessageFacade messageFacade,
BlockChainFacade blockChainFacade,
MockWalletFacade walletFacade,
ICryptoFacade cryptoFacade)
{
this.user = user;
this.settings = settings;
this.messageFacade = messageFacade;
this.blockChainFacade = blockChainFacade;
this.walletFacade = walletFacade;
this.cryptoFacade = cryptoFacade;
}
/**
* @param offer
*/
public void placeNewOffer(Offer offer)
{
log.info("place New Offer");
offers.put(offer.getUid().toString(), offer);
messageFacade.broadcast(new Message(Message.BROADCAST_NEW_OFFER, offer));
}
/**
* Taker requests offerer to take the offer
*
* @param trade
*/
public void sendTakeOfferRequest(Trade trade)
{
log.info("Taker asks offerer to take his offer");
messageFacade.send(new Message(Message.REQUEST_TAKE_OFFER, trade), trade.getOffer().getOfferer().getMessageID());
}
/**
* @param trade
* @return
*/
public Contract createNewContract(Trade trade)
{
log.info("create new contract");
KeyPair address = walletFacade.createNewAddress();
Contract contract = new Contract(trade, address.getPubKey());
contract.setOfferer(trade.getOffer().getOfferer());
contract.setTaker(user);
contracts.put(trade.getUid().toString(), contract);
return contract;
}
/**
* @param contract
*/
public void signContract(Contract contract)
{
log.info("sign Contract");
String contractAsJson = Utils.convertToJson(contract);
contract.getTrade().setJsonRepresentation(contractAsJson);
contract.getTrade().setSignature(cryptoFacade.sign(contractAsJson));
}
/**
* @param offer
* @return
*/
public Trade createNewTrade(Offer offer)
{
log.info("create New Trade");
Trade trade = new Trade(offer);
trades.put(trade.getUid().toString(), trade);
return trade;
}
public HashMap<String, Trade> getTrades()
{
return trades;
}
/**
* @param trade
*/
public void payOfferFee(Trade trade)
{
log.info("Pay offer fee");
trade.setTakeOfferFeePayed(true);
String txID = UUID.randomUUID().toString();
trade.setTakeOfferFeePayed(true);
trade.setTakeOfferFeeTxID(txID);
log.info("Taker asks offerer for confirmation for his fee payment. txID=" + txID);
messageFacade.send(new Message(Message.REQUEST_OFFER_FEE_PAYMENT_CONFIRM, trade), trade.getOffer().getOfferer().getMessageID());
}
public void requestOffererDetailData()
{
log.info("Request offerer detail data");
}
/**
*
* @param trade
*/
public void payToDepositTx(Trade trade)
{
log.info("create MultiSig address");
log.info("Create deposit tx");
log.info("Sign deposit tx");
log.info("Send deposit Tx");
messageFacade.send(new Message(Message.REQUEST_OFFER_FEE_PAYMENT_CONFIRM, trade), trade.getOffer().getOfferer().getMessageID());
}
/**
*
* @param trade
*/
public void releaseBTC(Trade trade)
{
log.info("Sign payment tx");
log.info("Broadcast payment tx");
log.info("Send message to peer that payment Tx has been broadcasted.");
messageFacade.send(new Message(Message.REQUEST_OFFER_FEE_PAYMENT_CONFIRM, trade), trade.getOffer().getOfferer().getMessageID());
}
}

View File

@ -0,0 +1,9 @@
package io.bitsquare.trade.orderbook;
import io.bitsquare.gui.trade.orderbook.OrderBookListItem;
import javafx.collections.ObservableList;
public interface IOrderBook
{
ObservableList<OrderBookListItem> getFilteredList(OrderBookFilter orderBookFilter);
}

View File

@ -0,0 +1,205 @@
package io.bitsquare.trade.orderbook;
import com.google.inject.Inject;
import io.bitsquare.gui.trade.orderbook.OrderBookListItem;
import io.bitsquare.gui.util.Converter;
import io.bitsquare.gui.util.Formatter;
import io.bitsquare.settings.Settings;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.Offer;
import io.bitsquare.trade.OfferConstraints;
import io.bitsquare.user.BankDetails;
import io.bitsquare.user.User;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import java.util.*;
import java.util.function.Predicate;
public class MockOrderBook implements IOrderBook
{
private ObservableList<OrderBookListItem> orderBookListItems;
private Settings settings;
@Inject
public MockOrderBook(Settings settings)
{
this.settings = settings;
orderBookListItems = FXCollections.observableArrayList();
for (int i = 0; i < 100; i++)
{
orderBookListItems.add(getOfferListVO());
}
}
@Override
public ObservableList<OrderBookListItem> getFilteredList(OrderBookFilter orderBookFilter)
{
FilteredList filtered = orderBookListItems.filtered(new Predicate<OrderBookListItem>()
{
@Override
public boolean test(OrderBookListItem offerListVO)
{
boolean priceResult;
boolean amountResult = offerListVO.getOffer().getAmount() >= orderBookFilter.getAmount();
// swap direction. use who want to buy btc want to see sell offers...
boolean directionResult = offerListVO.getOffer().getDirection() != orderBookFilter.getDirection();
boolean currencyResult = offerListVO.getOffer().getCurrency().equals(orderBookFilter.getCurrency());
if (offerListVO.getOffer().getDirection() == Direction.BUY && orderBookFilter.getPrice() > 0)
priceResult = offerListVO.getOffer().getPrice() <= orderBookFilter.getPrice();
else
priceResult = offerListVO.getOffer().getPrice() >= orderBookFilter.getPrice();
return priceResult && amountResult && directionResult && currencyResult;
}
});
//TODO use FilteredList
ObservableList<OrderBookListItem> result = FXCollections.observableArrayList();
result.addAll(filtered);
return result;
}
private OrderBookListItem getOfferListVO()
{
Offer i = getOffer();
return new OrderBookListItem(i);
}
public ArrayList<Currency> getCurrencies()
{
ArrayList<Currency> currencies = new ArrayList<>();
currencies.add(Currency.getInstance("USD"));
currencies.add(Currency.getInstance("EUR"));
currencies.add(Currency.getInstance("CNY"));
currencies.add(Currency.getInstance("RUB"));
currencies.add(Currency.getInstance("JPY"));
currencies.add(Currency.getInstance("GBP"));
currencies.add(Currency.getInstance("CAD"));
currencies.add(Currency.getInstance("AUD"));
currencies.add(Currency.getInstance("CHF"));
currencies.add(Currency.getInstance("CNY"));
/* Set<Currency> otherCurrenciesSet = Currency.getAvailableCurrencies();
ArrayList<Currency> otherCurrenciesList = new ArrayList<>();
otherCurrenciesList.addAll(otherCurrenciesSet);
Collections.sort(otherCurrenciesList, new CurrencyComparator());
currencies.addAll(otherCurrenciesList); */
return currencies;
}
private Offer getOffer()
{
double amount = Math.random() * 10 + 0.1;
amount = Converter.convertToDouble(Formatter.formatAmount(amount));
double minAmount = Math.random() * amount;
minAmount = Converter.convertToDouble(Formatter.formatAmount(minAmount));
String country = getCountries().get(0);
BankDetails bankDetails = new BankDetails();
String bankTransferType = getBankTransferTypes().get(0);
bankDetails.setBankTransferType(bankTransferType);
User offerer = new User(UUID.randomUUID().toString(), UUID.randomUUID().toString(), country, bankDetails);
Direction direction = Direction.BUY;
double price = 500 + Math.random() * 50;
if (Math.random() > 0.5)
{
direction = Direction.SELL;
price = 500 - Math.random() * 50;
}
Currency currency = (randomizeCurrencies(getCurrencies(), false)).get(0);
return new Offer(UUID.randomUUID(),
direction,
price,
amount,
minAmount,
currency,
offerer,
getRandomOfferConstraints()
);
}
public OfferConstraints getRandomOfferConstraints()
{
OfferConstraints offerConstraints = new OfferConstraints(getCountries(),
getLanguages(),
Double.valueOf(getCollaterals().get(0)),
getBankTransferTypes(),
getArbitrators().get(0),
randomizeStrings(settings.getAllIdentityVerifications(), false).get(0));
return offerConstraints;
}
private List<String> getCountries()
{
return randomizeStrings(settings.getAllCountries(), false);
}
private List<String> getLanguages()
{
return randomizeStrings(settings.getAllLanguages(), false);
}
private List<String> getBankTransferTypes()
{
return randomizeStrings(settings.getAllBankTransferTypes(), false);
}
private List<String> getArbitrators()
{
return randomizeStrings(settings.getAllArbitrators(), false);
}
private List<String> getCollaterals()
{
return randomizeStrings(settings.getAllCollaterals(), false);
}
private List<String> randomizeStrings(List<String> list, boolean optional)
{
int e = new Random().nextInt(list.size());
if (!optional && list.size() > 0)
e = Math.max(e, 1);
int s = (e == 0) ? 0 : new Random().nextInt(e);
list = list.subList(s, e);
return list;
}
private List<Double> randomizeDouble(List<Double> list, boolean optional)
{
int e = new Random().nextInt(list.size());
if (!optional && list.size() > 0)
e = Math.max(e, 1);
int s = (e == 0) ? 0 : new Random().nextInt(e);
list = list.subList(s, e);
return list;
}
private List<Currency> randomizeCurrencies(List<Currency> list, boolean optional)
{
int e = new Random().nextInt(list.size());
if (!optional && list.size() > 0)
e = Math.max(e, 1);
int s = (e == 0) ? 0 : new Random().nextInt(e);
list = list.subList(s, e);
return list;
}
}
class CurrencyComparator implements Comparator<Currency>
{
@Override
public int compare(Currency a, Currency b)
{
return a.getCurrencyCode().compareTo(b.getCurrencyCode());
}
}

View File

@ -0,0 +1,74 @@
package io.bitsquare.trade.orderbook;
import io.bitsquare.trade.Direction;
import io.bitsquare.trade.OfferConstraints;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import java.util.Currency;
public class OrderBookFilter
{
private double price;
private double amount;
private Direction direction;
private Currency currency;
private final StringProperty currencyProperty = new SimpleStringProperty();
private OfferConstraints offerConstraints;
public void setAmount(double amount)
{
this.amount = amount;
}
public double getAmount()
{
return amount;
}
public void setDirection(Direction direction)
{
this.direction = direction;
}
public Direction getDirection()
{
return direction;
}
public double getPrice()
{
return price;
}
public void setPrice(double price)
{
this.price = price;
}
public Currency getCurrency()
{
return currency;
}
public StringProperty getCurrencyProperty()
{
return currencyProperty;
}
public void setCurrency(Currency currency)
{
this.currency = currency;
currencyProperty.set(currency.getCurrencyCode());
}
public OfferConstraints getOfferConstraints()
{
return offerConstraints;
}
public void setOfferConstraints(OfferConstraints offerConstraints)
{
this.offerConstraints = offerConstraints;
}
}

View File

@ -0,0 +1,40 @@
package io.bitsquare.trade.payment.process;
public class BuyOffererPaymentProcess extends PaymentProcess
{
public BuyOffererPaymentProcess()
{
super();
}
// case 1 offerer step 1
private void buyOfferOfferer_payToDeposit()
{
onDataDepositTx();
payCollateral();
signDepositTx();
publishDepositTx();
sendMessageDepositTxPublished();
onBlockChainConfirmation();
}
// case 1 offerer step 2
private void buyOfferOfferer_payToDeposist()
{
payFiat();
sendMessageFiatTxInited();
createPayoutTx();
signPayoutTx();
sendDataPayoutTx();
onBlockChainConfirmation();
}
// case 1 offerer step 3
private void buyOfferOfferer_waitForRelease()
{
onMessagePayoutTxPublished();
onBlockChainConfirmation();
done();
}
}

View File

@ -0,0 +1,39 @@
package io.bitsquare.trade.payment.process;
public class BuyTakerPaymentProcess extends PaymentProcess
{
public BuyTakerPaymentProcess()
{
super();
}
@Override
public void executeStep0()
{
// bitcoinServices.createMultiSig();
createDepositTx();
payPaymentAndCollateral();
signDepositTx();
sendDataDepositTx();
}
@Override
public void executeStep1()
{
onMessageDepositTxPublished();
onMessageFiatTxInited();
onUserInputFiatReceived();
onDataPayoutTx();
}
@Override
public void executeStep2()
{
signPayoutTx();
publishPayoutTx();
sendMessagePayoutTxPublished();
onBlockChainConfirmation();
done();
}
}

View File

@ -0,0 +1,244 @@
package io.bitsquare.trade.payment.process;
import com.google.inject.Inject;
import io.bitsquare.btc.BlockChainFacade;
import io.bitsquare.btc.MockWalletFacade;
import io.bitsquare.msg.IMessageFacade;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PaymentProcess
{
private static final Logger log = LoggerFactory.getLogger(PaymentProcess.class);
private IMessageFacade messageService;
private BlockChainFacade bitcoinServices;
protected String offererDepositPubKey;
protected String offererPayoutAddress;
protected String offererChangeAddress;
protected String offererTotalInputPayment;
protected String offererOutputPayment;
protected String takerDepositPubKey;
protected String takerPayoutAddress;
protected String takerChangeAddress;
protected String takerTotalInputPayment;
protected String takerOutputPayment;
protected String multiSigAddress;
private MockWalletFacade wallet;
public PaymentProcess()
{
}
@Inject
public void setMessageService(IMessageFacade messageService)
{
this.messageService = messageService;
}
@Inject
public void setWallet(MockWalletFacade wallet)
{
this.wallet = wallet;
}
@Inject
public void setBtcServices(BlockChainFacade bitcoinServices)
{
this.bitcoinServices = bitcoinServices;
}
public void executeStep0()
{
}
public void executeStep1()
{
}
public void executeStep2()
{
}
public void executeStep3()
{
}
protected void createDepositTx()
{
//wallet.getInputs(offererTotalInputPayment);
//bitcoinServices.createTx(taker);
}
protected void payPaymentAndCollateral()
{
}
protected void signDepositTx()
{
}
protected void sendDataDepositTx()
{
}
protected void onMessageFiatTxInited()
{
}
protected void onUserInputFiatReceived()
{
}
protected void onDataPayoutTx()
{
}
protected void onMessageDepositTxPublished()
{
}
protected void signPayoutTx()
{
}
protected void publishPayoutTx()
{
}
protected void sendMessagePayoutTxPublished()
{
}
protected void onBlockChainConfirmation()
{
}
protected void done()
{
}
protected void onDataDepositTx()
{
}
protected void payCollateral()
{
}
protected void publishDepositTx()
{
}
protected void sendMessageDepositTxPublished()
{
}
protected void payFiat()
{
}
protected void sendMessageFiatTxInited()
{
}
protected void createPayoutTx()
{
}
protected void sendDataPayoutTx()
{
}
protected void onMessagePayoutTxPublished()
{
}
/*
case 1:
BUY offer
taker:
1 PAY BTC
create ms
create deposit tx
pay payment+coll
signContract
send deposit tx to offerer
2 WAIT FOR FIAT
wait for pub tx info msg
wait for build fiat info msg
wait for fiat on bank
wait for payout tx
3 RELEASE BTC
signContract payout tx
pub payout tx
send info to offerer
wait for >= 1 confirm
DONE
offerer:
1 WAIT FOR BTC PAYMENT
wait for deposit tx
pay coll
signContract
pub deposit tx
send info msg to taker
wait for >=1 confirm
2 PAY FIAT
build fiat
send info msg to taker
create payout tx
signContract payout tx
send payout tx to taker
3 WAIT FOR BTC RELEASE
wait for release info msg
wait for >= 1 confirm
DONE
case 2:
SELL offer
taker:
1 PAY COLL
create ms
create deposit tx
pay coll
signContract
send deposit tx to offerer
2 WAIT FOR BTC PAYMENT
wait for pub tx info msg
wait for >=1 confirm
3 PAY FIAT -> Same
build fiat
send info msg to taker
create payout tx
signContract payout tx
send payout tx to offerer
4 WAIT FOR BTC RELEASE -> Same
wait for release info msg
wait for >= 1 confirm
DONE
offerer:
1 WAIT FOR COLL
wait for deposit tx
2 PAY BTC
pay coll+payment
signContract
pub deposit tx
send info msg to taker
3 WAIT FOR FIAT
wait for build fiat info msg
wait for payout tx
wait for fiat on bank
4 RELEASE BTC
signContract payout tx
pub payout tx
send info to taker
wait for >= 1 confirm
DONE
*/
}

View File

@ -0,0 +1,44 @@
package io.bitsquare.trade.payment.process;
public class SellOffererPaymentProcess extends PaymentProcess
{
public SellOffererPaymentProcess()
{
super();
}
// case 2 offerer step 1
private void sellOfferOfferer_waitForCollateralPayedByPeer()
{
onDataDepositTx();
}
// case 2 offerer step 2
private void sellOfferOfferer_payToDeposit()
{
payPaymentAndCollateral();
signDepositTx();
publishDepositTx();
sendMessageDepositTxPublished();
}
// case 2 offerer step 3
private void sellOfferOfferer_waitForFiat()
{
onMessageFiatTxInited();
onDataPayoutTx();
onUserInputFiatReceived();
}
// case 2 offerer step 4
private void sellOfferOfferer_releasePayment()
{
signPayoutTx();
publishPayoutTx();
sendMessagePayoutTxPublished();
onBlockChainConfirmation();
done();
}
}

View File

@ -0,0 +1,45 @@
package io.bitsquare.trade.payment.process;
public class SellTakerPaymentProcess extends PaymentProcess
{
public SellTakerPaymentProcess()
{
super();
}
// case 2 taker step 1
private void sellOfferTaker_payToDeposit()
{
//createMultiSig();
createDepositTx();
payCollateral();
signDepositTx();
sendDataDepositTx();
}
// case 2 taker step 2
private void sellOfferTaker_waitForDepositPublished()
{
onMessageDepositTxPublished();
onBlockChainConfirmation();
}
// case 2 taker step 3
private void sellOfferTaker_payFiat()
{
payFiat();
sendMessageFiatTxInited();
createPayoutTx();
signPayoutTx();
sendDataPayoutTx();
}
// case 2 taker step 4
private void sellOfferTaker_waitForRelease()
{
onMessagePayoutTxPublished();
onBlockChainConfirmation();
done();
}
}

View File

@ -0,0 +1,61 @@
package io.bitsquare.user;
public class BankDetails
{
private String bankTransferType;
private String accountPrimaryID;
private String accountSecondaryID;
private String accountHolderName;
public BankDetails(String bankTransferType, String accountPrimaryID, String accountSecondaryID, String accountHolderName)
{
this.bankTransferType = bankTransferType;
this.accountPrimaryID = accountPrimaryID;
this.accountSecondaryID = accountSecondaryID;
this.accountHolderName = accountHolderName;
}
public BankDetails()
{
}
public void setBankTransferType(String bankTransferType)
{
this.bankTransferType = bankTransferType;
}
public String getAccountPrimaryID()
{
return accountPrimaryID;
}
public void setAccountPrimaryID(String accountPrimaryID)
{
this.accountPrimaryID = accountPrimaryID;
}
public String getAccountSecondaryID()
{
return accountSecondaryID;
}
public void setAccountSecondaryID(String accountSecondaryID)
{
this.accountSecondaryID = accountSecondaryID;
}
public String getAccountHolderName()
{
return accountHolderName;
}
public void setAccountHolderName(String accountHolderName)
{
this.accountHolderName = accountHolderName;
}
public String getBankTransferType()
{
return bankTransferType;
}
}

View File

@ -0,0 +1,74 @@
package io.bitsquare.user;
public class User
{
private String accountID;
private String messageID;
private boolean isOnline;
private BankDetails bankDetails;
private String country;
public User(String accountID, String messageID, String country, BankDetails bankDetails)
{
this.accountID = accountID;
this.messageID = messageID;
this.country = country;
this.bankDetails = bankDetails;
}
public User()
{
}
public String getMessageID()
{
return messageID;
}
public void setMessageID(String messageID)
{
this.messageID = messageID;
}
public String getAccountID()
{
return accountID;
}
public void setAccountID(String accountID)
{
this.accountID = accountID;
}
public void setCountry(String country)
{
this.country = country;
}
public String getCountry()
{
return country;
}
public BankDetails getBankDetails()
{
return bankDetails;
}
public void setBankDetails(BankDetails bankDetails)
{
this.bankDetails = bankDetails;
}
public boolean getOnline()
{
return isOnline;
}
public void setOnline(boolean online)
{
this.isOnline = online;
}
}

View File

@ -0,0 +1,43 @@
package io.bitsquare.util;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.*;
public class Utils
{
public static String convertToJson(Object object)
{
Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).create();
return gson.toJson(object);
}
public static Object copy(Object orig)
{
Object obj = null;
try
{
// Write the object out to a byte array
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(orig);
out.flush();
out.close();
// Make an input stream from the byte array and read
// a copy of the object back in.
ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
obj = in.readObject();
} catch (IOException e)
{
e.printStackTrace();
} catch (ClassNotFoundException cnfe)
{
cnfe.printStackTrace();
}
return obj;
}
}

View File

@ -0,0 +1,6 @@
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: Manfred Karrer
Build-Jdk: 1.8.0
Main-Class: io.bitsquare.BitSquare

View File

View File

@ -0,0 +1 @@
currency=Währung

View File

@ -0,0 +1 @@
currency=Currency

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 491 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

Some files were not shown because too many files have changed in this diff Show More