diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..89362a333a --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/docs +/log +/target +/bin +.idea +bitsquare.iml diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000..57418495d6 --- /dev/null +++ b/COPYING @@ -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. \ No newline at end of file diff --git a/README.md b/README.md index 9a84d8fb3f..ec5b58f6ca 100644 --- a/README.md +++ b/README.md @@ -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 + diff --git a/design/navIcons.psd b/design/navIcons.psd new file mode 100644 index 0000000000..bf371e550d Binary files /dev/null and b/design/navIcons.psd differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000..4baf9de8df --- /dev/null +++ b/pom.xml @@ -0,0 +1,89 @@ + + + 4.0.0 + + bitsquare + bitsquare + 0.1 + + BitSquare + A P2P Fiat-Bitcoin Exchange + https://www.bitsquare.io + + + + Apache 2 + http://www.apache.org/licenses/LICENSE-2.0 + repo + + + + + https://github.com/bitsquare/bitsquare + + + + + + src/main/java + + + src/main/resources + + + + + + + junit + junit + 4.11 + test + + + + org.slf4j + slf4j-api + 1.7.6 + + + + ch.qos.logback + logback-classic + 1.0.9 + compile + + + + com.google.inject + guice + 3.0 + + + + com.google.guava + guava-base + r03 + + + + com.google.code.gson + gson + 2.2.4 + compile + + + + org.controlsfx + controlsfx + 8.0.5 + + + + + + UTF-8 + + \ No newline at end of file diff --git a/src/main/java/io/bitsquare/BitSquare.java b/src/main/java/io/bitsquare/BitSquare.java new file mode 100644 index 0000000000..68293cf0f6 --- /dev/null +++ b/src/main/java/io/bitsquare/BitSquare.java @@ -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(); + } + } + +} diff --git a/src/main/java/io/bitsquare/btc/BlockChainFacade.java b/src/main/java/io/bitsquare/btc/BlockChainFacade.java new file mode 100644 index 0000000000..0e8c1a1c52 --- /dev/null +++ b/src/main/java/io/bitsquare/btc/BlockChainFacade.java @@ -0,0 +1,17 @@ +package io.bitsquare.btc; + +import com.google.inject.Inject; + +/** + * Gateway to blockchain + */ +public class BlockChainFacade +{ + @Inject + public BlockChainFacade() + { + + } + + +} diff --git a/src/main/java/io/bitsquare/btc/BtcFormatter.java b/src/main/java/io/bitsquare/btc/BtcFormatter.java new file mode 100644 index 0000000000..9a6d2cf9d8 --- /dev/null +++ b/src/main/java/io/bitsquare/btc/BtcFormatter.java @@ -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(); + } +} diff --git a/src/main/java/io/bitsquare/btc/Fees.java b/src/main/java/io/bitsquare/btc/Fees.java new file mode 100644 index 0000000000..b3cdbc5482 --- /dev/null +++ b/src/main/java/io/bitsquare/btc/Fees.java @@ -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"); + +} diff --git a/src/main/java/io/bitsquare/btc/IWalletFacade.java b/src/main/java/io/bitsquare/btc/IWalletFacade.java new file mode 100644 index 0000000000..3981da16bb --- /dev/null +++ b/src/main/java/io/bitsquare/btc/IWalletFacade.java @@ -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(); +} diff --git a/src/main/java/io/bitsquare/btc/KeyPair.java b/src/main/java/io/bitsquare/btc/KeyPair.java new file mode 100644 index 0000000000..67a60eab20 --- /dev/null +++ b/src/main/java/io/bitsquare/btc/KeyPair.java @@ -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; + } +} diff --git a/src/main/java/io/bitsquare/btc/MockWalletFacade.java b/src/main/java/io/bitsquare/btc/MockWalletFacade.java new file mode 100644 index 0000000000..3443a2bdbe --- /dev/null +++ b/src/main/java/io/bitsquare/btc/MockWalletFacade.java @@ -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()); + } +} diff --git a/src/main/java/io/bitsquare/crypto/ICryptoFacade.java b/src/main/java/io/bitsquare/crypto/ICryptoFacade.java new file mode 100644 index 0000000000..53e1c1cbad --- /dev/null +++ b/src/main/java/io/bitsquare/crypto/ICryptoFacade.java @@ -0,0 +1,6 @@ +package io.bitsquare.crypto; + +public interface ICryptoFacade +{ + String sign(String data); +} diff --git a/src/main/java/io/bitsquare/crypto/MockCryptoFacade.java b/src/main/java/io/bitsquare/crypto/MockCryptoFacade.java new file mode 100644 index 0000000000..32642b0bd0 --- /dev/null +++ b/src/main/java/io/bitsquare/crypto/MockCryptoFacade.java @@ -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"; + } +} diff --git a/src/main/java/io/bitsquare/di/BitSquareModule.java b/src/main/java/io/bitsquare/di/BitSquareModule.java new file mode 100644 index 0000000000..4190481eb1 --- /dev/null +++ b/src/main/java/io/bitsquare/di/BitSquareModule.java @@ -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(); + } +} \ No newline at end of file diff --git a/src/main/java/io/bitsquare/di/GuiceControllerFactory.java b/src/main/java/io/bitsquare/di/GuiceControllerFactory.java new file mode 100644 index 0000000000..be18d87f14 --- /dev/null +++ b/src/main/java/io/bitsquare/di/GuiceControllerFactory.java @@ -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)}. + *

+ * Once set, make sure you do not use the static methods on + * {@link javafx.fxml.FXMLLoader} when creating your JavaFX node. + */ +public class GuiceControllerFactory implements Callback, Object> +{ + + private Injector injector; + + public GuiceControllerFactory(Injector injector) + { + this.injector = injector; + } + + @Override + public Object call(Class aClass) + { + return injector.getInstance(aClass); + } +} diff --git a/src/main/java/io/bitsquare/di/GuiceFXMLLoader.java b/src/main/java/io/bitsquare/di/GuiceFXMLLoader.java new file mode 100644 index 0000000000..6c80ccae9e --- /dev/null +++ b/src/main/java/io/bitsquare/di/GuiceFXMLLoader.java @@ -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)); + } + } +} diff --git a/src/main/java/io/bitsquare/gui/IChildController.java b/src/main/java/io/bitsquare/gui/IChildController.java new file mode 100644 index 0000000000..a55e5bc062 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/IChildController.java @@ -0,0 +1,6 @@ +package io.bitsquare.gui; + +public interface IChildController +{ + void setNavigationController(INavigationController navigationController); +} diff --git a/src/main/java/io/bitsquare/gui/INavigationController.java b/src/main/java/io/bitsquare/gui/INavigationController.java new file mode 100644 index 0000000000..5f47d087b6 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/INavigationController.java @@ -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); +} diff --git a/src/main/java/io/bitsquare/gui/MainController.java b/src/main/java/io/bitsquare/gui/MainController.java new file mode 100644 index 0000000000..7125b4b684 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/MainController.java @@ -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() + { + @Override + public void changed(ObservableValue ov, Currency oldValue, Currency newValue) + { + orderBookFilter.setCurrency(newValue); + settings.setCurrency(newValue); + } + }); + holder.getChildren().add(currencyComboBox); + rightNavPane.getChildren().add(holder); + } +} \ No newline at end of file diff --git a/src/main/java/io/bitsquare/gui/MainView.fxml b/src/main/java/io/bitsquare/gui/MainView.fxml new file mode 100644 index 0000000000..f3b9269c29 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/MainView.fxml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/src/main/java/io/bitsquare/gui/components/BuySellSwitch.java b/src/main/java/io/bitsquare/gui/components/BuySellSwitch.java new file mode 100644 index 0000000000..770be531f0 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/components/BuySellSwitch.java @@ -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); + } + }); + } + + +} diff --git a/src/main/java/io/bitsquare/gui/components/HSpacer.java b/src/main/java/io/bitsquare/gui/components/HSpacer.java new file mode 100644 index 0000000000..479e51002e --- /dev/null +++ b/src/main/java/io/bitsquare/gui/components/HSpacer.java @@ -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(); + } +} + diff --git a/src/main/java/io/bitsquare/gui/components/NoFocusScrollPane.java b/src/main/java/io/bitsquare/gui/components/NoFocusScrollPane.java new file mode 100644 index 0000000000..b32695d188 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/components/NoFocusScrollPane.java @@ -0,0 +1,11 @@ +package io.bitsquare.gui.components; + +import javafx.scene.control.ScrollPane; + +public class NoFocusScrollPane extends ScrollPane +{ + public void requestFocus() + { + // prevent focus + } +} diff --git a/src/main/java/io/bitsquare/gui/components/PTableColumn.java b/src/main/java/io/bitsquare/gui/components/PTableColumn.java new file mode 100644 index 0000000000..163c41f0a9 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/components/PTableColumn.java @@ -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 extends javafx.scene.control.TableColumn +{ + + private final DoubleProperty percentageWidth = new SimpleDoubleProperty(0); + + public PTableColumn() + { + tableViewProperty().addListener(new ChangeListener>() + { + + @Override + public void changed(ObservableValue> ov, TableView t, TableView 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)); + } +} diff --git a/src/main/java/io/bitsquare/gui/components/VSpacer.java b/src/main/java/io/bitsquare/gui/components/VSpacer.java new file mode 100644 index 0000000000..d0dc7f339a --- /dev/null +++ b/src/main/java/io/bitsquare/gui/components/VSpacer.java @@ -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(); + } +} + diff --git a/src/main/java/io/bitsquare/gui/components/processbar/ProcessStepBar.java b/src/main/java/io/bitsquare/gui/components/processbar/ProcessStepBar.java new file mode 100644 index 0000000000..63ce9eae25 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/components/processbar/ProcessStepBar.java @@ -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 extends Control +{ + private List processStepItems; + + public ProcessStepBar(List processStepItems) + { + this.processStepItems = processStepItems; + } + + @Override + protected Skin createDefaultSkin() + { + return new ProcessStepBarSkin<>(this); + } + + List getProcessStepItems() + { + return processStepItems; + } + + public void next() + { + ((ProcessStepBarSkin) getSkin()).next(); + } +} diff --git a/src/main/java/io/bitsquare/gui/components/processbar/ProcessStepBarSkin.java b/src/main/java/io/bitsquare/gui/components/processbar/ProcessStepBarSkin.java new file mode 100644 index 0000000000..d89c7f63e4 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/components/processbar/ProcessStepBarSkin.java @@ -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 extends BehaviorSkinBase, BehaviorBase>> +{ + LabelWithBorder currentLabelWithBorder; + LabelWithBorder prevLabelWithBorder; + final ProcessStepBar controller; + int index; + List labelWithBorders; + + public ProcessStepBarSkin(final ProcessStepBar control) + { + super(control, new BehaviorBase<>(control, Collections.emptyList())); + + controller = getSkinnable(); + + int i = 0; + labelWithBorders = new ArrayList<>(); + int size = controller.getProcessStepItems().size(); + for (Iterator 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; + } + + + } +} diff --git a/src/main/java/io/bitsquare/gui/components/processbar/ProcessStepItem.java b/src/main/java/io/bitsquare/gui/components/processbar/ProcessStepItem.java new file mode 100644 index 0000000000..a953f76c0d --- /dev/null +++ b/src/main/java/io/bitsquare/gui/components/processbar/ProcessStepItem.java @@ -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; + } +} diff --git a/src/main/java/io/bitsquare/gui/components/processbar/ProcessStepsBuilder.java b/src/main/java/io/bitsquare/gui/components/processbar/ProcessStepsBuilder.java new file mode 100644 index 0000000000..30503730c3 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/components/processbar/ProcessStepsBuilder.java @@ -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 processStepItems = new ArrayList(); + protected ProcessStepBar 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; + } + } + } +} + diff --git a/src/main/java/io/bitsquare/gui/funds/FundsController.java b/src/main/java/io/bitsquare/gui/funds/FundsController.java new file mode 100644 index 0000000000..f693c94580 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/funds/FundsController.java @@ -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; + } +} + diff --git a/src/main/java/io/bitsquare/gui/funds/FundsView.fxml b/src/main/java/io/bitsquare/gui/funds/FundsView.fxml new file mode 100644 index 0000000000..72ce4696a6 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/funds/FundsView.fxml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/main/java/io/bitsquare/gui/global.css b/src/main/java/io/bitsquare/gui/global.css new file mode 100644 index 0000000000..8c299a3f84 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/global.css @@ -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; +} diff --git a/src/main/java/io/bitsquare/gui/history/HistoryController.java b/src/main/java/io/bitsquare/gui/history/HistoryController.java new file mode 100644 index 0000000000..4943bfacc1 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/history/HistoryController.java @@ -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; + } +} + diff --git a/src/main/java/io/bitsquare/gui/history/HistoryView.fxml b/src/main/java/io/bitsquare/gui/history/HistoryView.fxml new file mode 100644 index 0000000000..0b9aaff1b3 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/history/HistoryView.fxml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/main/java/io/bitsquare/gui/home/HomeController.java b/src/main/java/io/bitsquare/gui/home/HomeController.java new file mode 100644 index 0000000000..9ad52f01bd --- /dev/null +++ b/src/main/java/io/bitsquare/gui/home/HomeController.java @@ -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; + } + +} + diff --git a/src/main/java/io/bitsquare/gui/home/HomeView.fxml b/src/main/java/io/bitsquare/gui/home/HomeView.fxml new file mode 100644 index 0000000000..90c0ea8209 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/home/HomeView.fxml @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/src/main/java/io/bitsquare/gui/msg/MockDelay.java b/src/main/java/io/bitsquare/gui/msg/MockDelay.java new file mode 100644 index 0000000000..5ca9ffee47 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/msg/MockDelay.java @@ -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 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 +{ + private String expectedMsg; + + Task(String expectedMsg) + { + this.expectedMsg = expectedMsg; + } + + @Override + public String call() throws Exception + { + Thread.sleep(1000); // 1 seconds pause + return expectedMsg; + } +} \ No newline at end of file diff --git a/src/main/java/io/bitsquare/gui/msg/MsgController.java b/src/main/java/io/bitsquare/gui/msg/MsgController.java new file mode 100644 index 0000000000..84a0af6993 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/msg/MsgController.java @@ -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; + } + +} + diff --git a/src/main/java/io/bitsquare/gui/msg/MsgView.fxml b/src/main/java/io/bitsquare/gui/msg/MsgView.fxml new file mode 100644 index 0000000000..e226640953 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/msg/MsgView.fxml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/main/java/io/bitsquare/gui/orders/OrdersController.java b/src/main/java/io/bitsquare/gui/orders/OrdersController.java new file mode 100644 index 0000000000..9e6898df3a --- /dev/null +++ b/src/main/java/io/bitsquare/gui/orders/OrdersController.java @@ -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; + } + +} + diff --git a/src/main/java/io/bitsquare/gui/orders/OrdersView.fxml b/src/main/java/io/bitsquare/gui/orders/OrdersView.fxml new file mode 100644 index 0000000000..7f975defbf --- /dev/null +++ b/src/main/java/io/bitsquare/gui/orders/OrdersView.fxml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/main/java/io/bitsquare/gui/settings/SettingsController.java b/src/main/java/io/bitsquare/gui/settings/SettingsController.java new file mode 100644 index 0000000000..fde025b2f7 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/settings/SettingsController.java @@ -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; + } + +} + diff --git a/src/main/java/io/bitsquare/gui/settings/SettingsView.fxml b/src/main/java/io/bitsquare/gui/settings/SettingsView.fxml new file mode 100644 index 0000000000..3666574c98 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/settings/SettingsView.fxml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/main/java/io/bitsquare/gui/trade/TradeController.java b/src/main/java/io/bitsquare/gui/trade/TradeController.java new file mode 100644 index 0000000000..3a9b6966af --- /dev/null +++ b/src/main/java/io/bitsquare/gui/trade/TradeController.java @@ -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); + } + +} + diff --git a/src/main/java/io/bitsquare/gui/trade/TradeView.fxml b/src/main/java/io/bitsquare/gui/trade/TradeView.fxml new file mode 100644 index 0000000000..47c670d0fa --- /dev/null +++ b/src/main/java/io/bitsquare/gui/trade/TradeView.fxml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/src/main/java/io/bitsquare/gui/trade/offer/CreateOfferController.java b/src/main/java/io/bitsquare/gui/trade/offer/CreateOfferController.java new file mode 100644 index 0000000000..c75333f7e7 --- /dev/null +++ b/src/main/java/io/bitsquare/gui/trade/offer/CreateOfferController.java @@ -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() + { + @Override + public void changed(ObservableValue observable, String oldValue, String newValue) + { + setVolume(); + } + }); + + price.textProperty().addListener(new ChangeListener() + { + @Override + public void changed(ObservableValue 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 currencies = orderBookFilterSettings.getCurrencies(); + Currency currency = orderBookFilterSettings.getCurrency(); + ComboBox currencyComboBox = createCurrencyItem("Currency: ", currency, currencies); + currencyComboBox.valueProperty().addListener(new ChangeListener() + { + @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 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 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)); + } + + +} + diff --git a/src/main/java/io/bitsquare/gui/trade/offer/CreateOfferView.fxml b/src/main/java/io/bitsquare/gui/trade/offer/CreateOfferView.fxml new file mode 100644 index 0000000000..e3c739201f --- /dev/null +++ b/src/main/java/io/bitsquare/gui/trade/offer/CreateOfferView.fxml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +