mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06:45 +00:00
transaction headers pane first draft
This commit is contained in:
parent
4826671fc3
commit
b6b18a2a7c
33 changed files with 885 additions and 76 deletions
|
@ -2,3 +2,8 @@
|
||||||
Bitcoin Transaction Editor
|
Bitcoin Transaction Editor
|
||||||
|
|
||||||
To clone this project, use `git clone --recursive git@github.com:craigraw/sparrow.git`
|
To clone this project, use `git clone --recursive git@github.com:craigraw/sparrow.git`
|
||||||
|
|
||||||
|
## Various ways to hex dump a file without spaces:
|
||||||
|
xxd -p file | tr -d '\n'
|
||||||
|
hexdump -ve '1/1 "%.2x"'
|
||||||
|
od -t x1 -An file | tr -d '\n '
|
|
@ -19,6 +19,8 @@ javafx {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation project(':drongo')
|
implementation project(':drongo')
|
||||||
|
compile group: 'org.fxmisc.richtext', name: 'richtextfx', version: '0.10.4'
|
||||||
|
compile group: 'no.tornado', name: 'tornadofx-controls', version: '1.0.4'
|
||||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
drongo
2
drongo
|
@ -1 +1 @@
|
||||||
Subproject commit 566aa2e95315291dd7c2092dfc1378bf2fa0d484
|
Subproject commit 7fb5601de38e79d5c0547ebc684ca137aa895134
|
|
@ -1,11 +1,12 @@
|
||||||
package com.craigraw.sparrow;
|
package com.craigraw.sparrow;
|
||||||
|
|
||||||
|
import com.craigraw.drongo.Utils;
|
||||||
|
import com.craigraw.drongo.protocol.Transaction;
|
||||||
import com.craigraw.drongo.psbt.PSBT;
|
import com.craigraw.drongo.psbt.PSBT;
|
||||||
import javafx.event.ActionEvent;
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.FXMLLoader;
|
import javafx.fxml.FXMLLoader;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
import javafx.scene.Parent;
|
|
||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
import javafx.scene.control.TabPane;
|
import javafx.scene.control.TabPane;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
|
@ -25,13 +26,20 @@ public class AppController implements Initializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
addExampleTxTabs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void open(ActionEvent event) {
|
public void open(ActionEvent event) {
|
||||||
Stage window = new Stage();
|
Stage window = new Stage();
|
||||||
|
|
||||||
FileChooser fileChooser = new FileChooser();
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
fileChooser.setTitle("Open Transaction");
|
||||||
|
fileChooser.getExtensionFilters().addAll(
|
||||||
|
new FileChooser.ExtensionFilter("All Files", "*.*"),
|
||||||
|
new FileChooser.ExtensionFilter("PSBT", "*.psbt"),
|
||||||
|
new FileChooser.ExtensionFilter("TXN", "*.txn")
|
||||||
|
);
|
||||||
|
|
||||||
File file = fileChooser.showOpenDialog(window);
|
File file = fileChooser.showOpenDialog(window);
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
System.out.println(file);
|
System.out.println(file);
|
||||||
|
@ -61,4 +69,29 @@ public class AppController implements Initializable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addExampleTxTabs() {
|
||||||
|
addTransactionTab("p2pkh", "01000000019c2e0f24a03e72002a96acedb12a632e72b6b74c05dc3ceab1fe78237f886c48010000006a47304402203da9d487be5302a6d69e02a861acff1da472885e43d7528ed9b1b537a8e2cac9022002d1bca03a1e9715a99971bafe3b1852b7a4f0168281cbd27a220380a01b3307012102c9950c622494c2e9ff5a003e33b690fe4832477d32c2d256c67eab8bf613b34effffffff02b6f50500000000001976a914bdf63990d6dc33d705b756e13dd135466c06b3b588ac845e0201000000001976a9145fb0e9755a3424efd2ba0587d20b1e98ee29814a88ac06241559");
|
||||||
|
addTransactionTab("p2sh-p2wpkh", "01000000000101db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a5477010000001716001479091972186c449eb1ded22b78e40d009bdf0089feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac02473044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb012103ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a2687392040000");
|
||||||
|
addTransactionTab("p2wpkh", "01000000000102fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f00000000494830450221008b9d1dc26ba6a9cb62127b02742fa9d754cd3bebf337f7a55d114c8e5cdd30be022040529b194ba3f9281a99f2b1c0a19c0489bc22ede944ccf4ecbab4cc618ef3ed01eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac000247304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee0121025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee635711000000");
|
||||||
|
addTransactionTab("p2wsh", "01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000");
|
||||||
|
addTransactionTab("test1", "02000000000102ba4dc5a4a14bfaa941b7d115b379b5e15f960635cf694c178b9116763cbd63b11600000017160014fc164cbcac023f5eacfcead2d17d8768c41949affeffffff074d44d2856beb68ba52e8832da60a1682768c2421c2d9a8109ef4e66babd1fd1e000000171600148c3098be6b430859115f5ee99c84c368afecd048feffffff02305310000000000017a914ffaf369c2212b178c7a2c21c9ccdd5d126e74c4187327f0300000000001976a914a7cda2e06b102a143ab606937a01d152e300cd3e88ac02473044022006da0ca227f765179219e08a33026b94e7cacff77f87b8cd8eb1b46d6dda11d6022064faa7912924fd23406b6ed3328f1bbbc3760dc51109a49c1b38bf57029d304f012103c6a2fcd030270427d4abe1041c8af929a9e2dbab07b243673453847ab842ee1f024730440220786316a16095105a0af28dccac5cf80f449dea2ea810a9559a89ecb989c2cb3d02205cbd9913d1217ffec144ae4f2bd895f16d778c2ec49ae9c929fdc8bcc2a2b1db0121024d4985241609d072a59be6418d700e87688f6c4d99a51ad68e66078211f076ee38820900");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTransactionTab(String name, String hex) {
|
||||||
|
try {
|
||||||
|
byte[] txbytes = Utils.hexToBytes(hex);
|
||||||
|
Transaction transaction = new Transaction(txbytes);
|
||||||
|
|
||||||
|
Tab tab = new Tab(name);
|
||||||
|
FXMLLoader transactionLoader = new FXMLLoader(getClass().getResource("transaction.fxml"));
|
||||||
|
tab.setContent(transactionLoader.load());
|
||||||
|
TransactionController controller = transactionLoader.getController();
|
||||||
|
controller.setTransaction(transaction);
|
||||||
|
|
||||||
|
tabs.getTabs().add(tab);
|
||||||
|
} catch(IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
31
src/main/java/com/craigraw/sparrow/EventManager.java
Normal file
31
src/main/java/com/craigraw/sparrow/EventManager.java
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package com.craigraw.sparrow;
|
||||||
|
|
||||||
|
import com.craigraw.drongo.protocol.Transaction;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class EventManager {
|
||||||
|
private List<TransactionListener> listenerList = new ArrayList<>();
|
||||||
|
private static EventManager SINGLETON = new EventManager();
|
||||||
|
|
||||||
|
private EventManager() {}
|
||||||
|
|
||||||
|
public void subscribe(TransactionListener listener) {
|
||||||
|
listenerList.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unsubscribe(TransactionListener listener) {
|
||||||
|
listenerList.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void notify(Transaction transaction) {
|
||||||
|
for (TransactionListener listener : listenerList) {
|
||||||
|
listener.updated(transaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EventManager get() {
|
||||||
|
return SINGLETON;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,46 +2,66 @@ package com.craigraw.sparrow;
|
||||||
|
|
||||||
import com.craigraw.drongo.protocol.*;
|
import com.craigraw.drongo.protocol.*;
|
||||||
import com.craigraw.drongo.psbt.PSBT;
|
import com.craigraw.drongo.psbt.PSBT;
|
||||||
|
import com.craigraw.sparrow.form.*;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
import javafx.scene.control.TreeCell;
|
|
||||||
import javafx.scene.control.TreeItem;
|
import javafx.scene.control.TreeItem;
|
||||||
import javafx.scene.control.TreeView;
|
import javafx.scene.control.TreeView;
|
||||||
import javafx.scene.control.cell.TextFieldTreeCell;
|
import javafx.scene.control.cell.TextFieldTreeCell;
|
||||||
import javafx.util.Callback;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.util.StringConverter;
|
import javafx.util.StringConverter;
|
||||||
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
|
import org.fxmisc.richtext.CodeArea;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
public class TransactionController implements Initializable {
|
public class TransactionController implements Initializable, TransactionListener {
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private TreeView<TransactionPart> txtree;
|
private TreeView<Form> txtree;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Pane txpane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private CodeArea txhex;
|
||||||
|
|
||||||
private Transaction transaction;
|
private Transaction transaction;
|
||||||
private PSBT psbt;
|
private PSBT psbt;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL location, ResourceBundle resources) {
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
EventManager.get().subscribe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initialiseTxTree() {
|
private void initializeView() {
|
||||||
TreeItem<TransactionPart> rootItem = new TreeItem<>(transaction);
|
initializeTxTree();
|
||||||
|
refreshTxHex();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeTxTree() {
|
||||||
|
HeadersForm headersForm = new HeadersForm(transaction, psbt);
|
||||||
|
TreeItem<Form> rootItem = new TreeItem<>(headersForm);
|
||||||
rootItem.setExpanded(true);
|
rootItem.setExpanded(true);
|
||||||
|
|
||||||
InputsPart inputsPart = new InputsPart();
|
InputsForm inputsForm = new InputsForm(transaction);
|
||||||
TreeItem<TransactionPart> inputsItem = new TreeItem<TransactionPart>(inputsPart);
|
TreeItem<Form> inputsItem = new TreeItem<>(inputsForm);
|
||||||
for(TransactionInput input : transaction.getInputs()) {
|
inputsItem.setExpanded(true);
|
||||||
TreeItem<TransactionPart> inputItem = new TreeItem<>(input);
|
for(TransactionInput txInput : transaction.getInputs()) {
|
||||||
|
InputForm inputForm = new InputForm(txInput);
|
||||||
|
TreeItem<Form> inputItem = new TreeItem<>(inputForm);
|
||||||
inputsItem.getChildren().add(inputItem);
|
inputsItem.getChildren().add(inputItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
OutputsPart outputsPart = new OutputsPart();
|
OutputsForm outputsForm = new OutputsForm(transaction);
|
||||||
TreeItem<TransactionPart> outputsItem = new TreeItem<TransactionPart>(outputsPart);
|
TreeItem<Form> outputsItem = new TreeItem<>(outputsForm);
|
||||||
for(TransactionOutput output : transaction.getOutputs()) {
|
outputsItem.setExpanded(true);
|
||||||
TreeItem<TransactionPart> outputItem = new TreeItem<>(output);
|
for(TransactionOutput txOutput : transaction.getOutputs()) {
|
||||||
|
OutputForm outputForm = new OutputForm(txOutput);
|
||||||
|
TreeItem<Form> outputItem = new TreeItem<>(outputForm);
|
||||||
outputsItem.getChildren().add(outputItem);
|
outputsItem.getChildren().add(outputItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,66 +69,113 @@ public class TransactionController implements Initializable {
|
||||||
rootItem.getChildren().add(outputsItem);
|
rootItem.getChildren().add(outputsItem);
|
||||||
txtree.setRoot(rootItem);
|
txtree.setRoot(rootItem);
|
||||||
|
|
||||||
txtree.setCellFactory(new Callback<TreeView<TransactionPart>, TreeCell<TransactionPart>>() {
|
txtree.setCellFactory(p -> new TextFieldTreeCell<>(new StringConverter<Form>(){
|
||||||
@Override
|
@Override
|
||||||
public TreeCell<TransactionPart> call(TreeView<TransactionPart> p) {
|
public String toString(Form form) {
|
||||||
return new TextFieldTreeCell<TransactionPart>(new StringConverter<TransactionPart>(){
|
return form.toString();
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(TransactionPart part) {
|
|
||||||
if(part instanceof Transaction) {
|
|
||||||
Transaction transaction = (Transaction)part;
|
|
||||||
return "Tx " + transaction.getTxId().toString().substring(0, 6) + "...";
|
|
||||||
} else if(part instanceof InputsPart) {
|
|
||||||
return "Inputs";
|
|
||||||
} else if(part instanceof OutputsPart) {
|
|
||||||
return "Outputs";
|
|
||||||
} else if(part instanceof TransactionInput) {
|
|
||||||
TransactionInput input = (TransactionInput)part;
|
|
||||||
return "Input #" + input.getIndex();
|
|
||||||
} else if(part instanceof TransactionOutput) {
|
|
||||||
TransactionOutput output = (TransactionOutput)part;
|
|
||||||
return "Output #" + output.getIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
return part.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransactionPart fromString(String string) {
|
public Form fromString(String string) {
|
||||||
throw new IllegalStateException("No fromString");
|
throw new IllegalStateException("No editing");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
txtree.getSelectionModel().selectedItemProperty().addListener((observable, old_val, new_val) -> {
|
||||||
|
Form form = new_val.getValue();
|
||||||
|
|
||||||
|
try {
|
||||||
|
txpane.getChildren().removeAll();
|
||||||
|
txpane.getChildren().add(form.getContents());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new IllegalStateException("Can't find pane", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
txtree.getSelectionModel().select(txtree.getRoot());
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
void refreshTxHex() {
|
||||||
|
txhex.clear();
|
||||||
|
|
||||||
|
String hex = "";
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
transaction.bitcoinSerializeToStream(baos);
|
||||||
|
hex = Hex.toHexString(baos.toByteArray());
|
||||||
|
} catch(IOException e) {
|
||||||
|
throw new IllegalStateException("Can't happen");
|
||||||
|
}
|
||||||
|
|
||||||
|
int cursor = 0;
|
||||||
|
|
||||||
|
//Version
|
||||||
|
cursor = addText(hex, cursor, 8, "version");
|
||||||
|
|
||||||
|
if(transaction.hasWitnesses()) {
|
||||||
|
//Segwit marker
|
||||||
|
cursor = addText(hex, cursor, 2, "segwit-marker");
|
||||||
|
//Segwit flag
|
||||||
|
cursor = addText(hex, cursor, 2, "segwit-flag");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Number of inputs
|
||||||
|
cursor = addText(hex, cursor, 2, "num-inputs");
|
||||||
|
|
||||||
|
//Inputs
|
||||||
|
int totalInputLength = 0;
|
||||||
|
for(TransactionInput input : transaction.getInputs()) {
|
||||||
|
totalInputLength += input.getLength();
|
||||||
|
}
|
||||||
|
cursor = addText(hex, cursor, totalInputLength*2, "inputs");
|
||||||
|
|
||||||
|
//Number of outputs
|
||||||
|
cursor = addText(hex, cursor, 2, "num-outputs");
|
||||||
|
|
||||||
|
//Outputs
|
||||||
|
int totalOutputLength = 0;
|
||||||
|
for(TransactionOutput output : transaction.getOutputs()) {
|
||||||
|
totalOutputLength += output.getLength();
|
||||||
|
}
|
||||||
|
cursor = addText(hex, cursor, totalOutputLength*2, "outputs");
|
||||||
|
|
||||||
|
if(transaction.hasWitnesses()) {
|
||||||
|
int totalWitnessLength = 0;
|
||||||
|
for(TransactionInput input : transaction.getInputs()) {
|
||||||
|
totalWitnessLength += input.getWitness().getLength();
|
||||||
|
}
|
||||||
|
cursor = addText(hex, cursor, totalWitnessLength*2, "witnesses");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Locktime
|
||||||
|
cursor = addText(hex, cursor, 8, "locktime");
|
||||||
|
|
||||||
|
if(cursor != hex.length()) {
|
||||||
|
throw new IllegalStateException("Cursor position does not match transaction serialisation " + cursor + ": " + hex.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int addText(String hex, int cursor, int length, String description) {
|
||||||
|
txhex.append(hex.substring(cursor, cursor+=length), description + "-color");
|
||||||
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPSBT(PSBT psbt) {
|
public void setPSBT(PSBT psbt) {
|
||||||
this.psbt = psbt;
|
this.psbt = psbt;
|
||||||
this.transaction = psbt.getTransaction();
|
this.transaction = psbt.getTransaction();
|
||||||
|
|
||||||
initialiseTxTree();
|
initializeView();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class InputsPart extends TransactionPart {
|
public void setTransaction(Transaction transaction) {
|
||||||
public InputsPart() {
|
this.transaction = transaction;
|
||||||
super(new byte[0], 0);
|
|
||||||
|
initializeView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void parse() throws ProtocolException {
|
public void updated(Transaction transaction) {
|
||||||
|
refreshTxHex();
|
||||||
}
|
txtree.refresh();
|
||||||
}
|
|
||||||
|
|
||||||
private static class OutputsPart extends TransactionPart {
|
|
||||||
public OutputsPart() {
|
|
||||||
super(new byte[0], 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void parse() throws ProtocolException {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.craigraw.sparrow;
|
||||||
|
|
||||||
|
import com.craigraw.drongo.protocol.Transaction;
|
||||||
|
|
||||||
|
public interface TransactionListener {
|
||||||
|
void updated(Transaction transaction);
|
||||||
|
}
|
9
src/main/java/com/craigraw/sparrow/form/Form.java
Normal file
9
src/main/java/com/craigraw/sparrow/form/Form.java
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package com.craigraw.sparrow.form;
|
||||||
|
|
||||||
|
import javafx.scene.Node;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public abstract class Form {
|
||||||
|
public abstract Node getContents() throws IOException;
|
||||||
|
}
|
157
src/main/java/com/craigraw/sparrow/form/HeadersController.java
Normal file
157
src/main/java/com/craigraw/sparrow/form/HeadersController.java
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
package com.craigraw.sparrow.form;
|
||||||
|
|
||||||
|
import com.craigraw.drongo.protocol.Transaction;
|
||||||
|
import com.craigraw.sparrow.EventManager;
|
||||||
|
import com.craigraw.sparrow.TransactionListener;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import tornadofx.control.DateTimePicker;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.time.*;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class HeadersController implements Initializable, TransactionListener {
|
||||||
|
private HeadersForm headersForm;
|
||||||
|
|
||||||
|
private static final long MAX_BLOCK_LOCKTIME = 500000000L;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private GridPane layout;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField idField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Spinner<Integer> versionField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label segwitField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label segwitVersionLabel;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Spinner<Integer> segwitVersionField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ToggleGroup locktimeTypeField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ToggleButton locktimeBlockTypeField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ToggleButton locktimeDateTypeField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Spinner<Integer> locktimeBlockField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private DateTimePicker locktimeDateField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label feeField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label sizeField;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label virtualSizeField;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
EventManager.get().subscribe(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setModel(HeadersForm form) {
|
||||||
|
this.headersForm = form;
|
||||||
|
Transaction tx = headersForm.getTransaction();
|
||||||
|
|
||||||
|
updateTxId();
|
||||||
|
|
||||||
|
versionField.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 2, (int)tx.getVersion()));
|
||||||
|
versionField.valueProperty().addListener((obs, oldValue, newValue) -> {
|
||||||
|
tx.setVersion(newValue);
|
||||||
|
EventManager.get().notify(tx);
|
||||||
|
});
|
||||||
|
|
||||||
|
segwitField.setText(tx.isSegwit() ? "Segwit" : "Legacy" );
|
||||||
|
if(tx.isSegwit()) {
|
||||||
|
segwitVersionField.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 2, tx.getSegwitVersion()));
|
||||||
|
segwitVersionField.valueProperty().addListener((obs, oldValue, newValue) -> {
|
||||||
|
tx.setSegwitVersion(newValue);
|
||||||
|
EventManager.get().notify(tx);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
layout.getChildren().removeAll(segwitVersionLabel, segwitVersionField);
|
||||||
|
}
|
||||||
|
|
||||||
|
locktimeTypeField.selectedToggleProperty().addListener((ov, old_toggle, new_toggle) -> {
|
||||||
|
if(locktimeTypeField.getSelectedToggle() != null) {
|
||||||
|
String selection = locktimeTypeField.getSelectedToggle().getUserData().toString();
|
||||||
|
if(selection.equals("block")) {
|
||||||
|
locktimeBlockField.setVisible(true);
|
||||||
|
locktimeDateField.setVisible(false);
|
||||||
|
Integer block = locktimeBlockField.getValue();
|
||||||
|
if(block != null) {
|
||||||
|
tx.setLockTime(block);
|
||||||
|
EventManager.get().notify(tx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
locktimeBlockField.setVisible(false);
|
||||||
|
locktimeDateField.setVisible(true);
|
||||||
|
LocalDateTime date = locktimeDateField.getDateTimeValue();
|
||||||
|
if(date != null) {
|
||||||
|
tx.setLockTime(date.toEpochSecond(OffsetDateTime.now(ZoneId.systemDefault()).getOffset()));
|
||||||
|
EventManager.get().notify(tx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if(tx.getLockTime() < MAX_BLOCK_LOCKTIME) {
|
||||||
|
locktimeBlockField.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(0, (int)MAX_BLOCK_LOCKTIME-1, (int)tx.getLockTime()));
|
||||||
|
locktimeBlockField.setVisible(true);
|
||||||
|
locktimeDateField.setVisible(false);
|
||||||
|
locktimeTypeField.selectToggle(locktimeBlockTypeField);
|
||||||
|
} else {
|
||||||
|
LocalDateTime date = Instant.ofEpochSecond(tx.getLockTime()).atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||||||
|
locktimeDateField.setDateTimeValue(date);
|
||||||
|
locktimeBlockField.setVisible(false);
|
||||||
|
locktimeDateField.setVisible(true);
|
||||||
|
locktimeTypeField.selectToggle(locktimeDateTypeField);
|
||||||
|
}
|
||||||
|
|
||||||
|
locktimeBlockField.valueProperty().addListener((obs, oldValue, newValue) -> {
|
||||||
|
tx.setLockTime(newValue);
|
||||||
|
EventManager.get().notify(tx);
|
||||||
|
});
|
||||||
|
|
||||||
|
locktimeDateField.setFormat("yyyy-MM-dd HH:mm:ss");
|
||||||
|
locktimeDateField.dateTimeValueProperty().addListener((obs, oldValue, newValue) -> {
|
||||||
|
tx.setLockTime(newValue.toEpochSecond(OffsetDateTime.now(ZoneId.systemDefault()).getOffset()));
|
||||||
|
EventManager.get().notify(tx);
|
||||||
|
});
|
||||||
|
|
||||||
|
if(form.getPsbt() != null) {
|
||||||
|
feeField.setText(form.getPsbt().getFee().toString() + " sats");
|
||||||
|
} else {
|
||||||
|
feeField.setText("Unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
sizeField.setText(tx.getSize() + " B");
|
||||||
|
|
||||||
|
virtualSizeField.setText(tx.getVirtualSize() + " vB");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTxId() {
|
||||||
|
idField.setText(headersForm.getTransaction().calculateTxId(false).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updated(Transaction transaction) {
|
||||||
|
updateTxId();
|
||||||
|
}
|
||||||
|
}
|
38
src/main/java/com/craigraw/sparrow/form/HeadersForm.java
Normal file
38
src/main/java/com/craigraw/sparrow/form/HeadersForm.java
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package com.craigraw.sparrow.form;
|
||||||
|
|
||||||
|
import com.craigraw.drongo.protocol.Transaction;
|
||||||
|
import com.craigraw.drongo.psbt.PSBT;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class HeadersForm extends Form {
|
||||||
|
private Transaction transaction;
|
||||||
|
private PSBT psbt;
|
||||||
|
|
||||||
|
public HeadersForm(Transaction transaction, PSBT psbt) {
|
||||||
|
this.transaction = transaction;
|
||||||
|
this.psbt = psbt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transaction getTransaction() {
|
||||||
|
return transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PSBT getPsbt() {
|
||||||
|
return psbt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getContents() throws IOException {
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("headers.fxml"));
|
||||||
|
Node node = loader.load();
|
||||||
|
HeadersController controller = loader.getController();
|
||||||
|
controller.setModel(this);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "Tx [" + transaction.calculateTxId(false).toString().substring(0, 6) + "]";
|
||||||
|
}
|
||||||
|
}
|
19
src/main/java/com/craigraw/sparrow/form/InputController.java
Normal file
19
src/main/java/com/craigraw/sparrow/form/InputController.java
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package com.craigraw.sparrow.form;
|
||||||
|
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class InputController implements Initializable {
|
||||||
|
private InputForm inputForm;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModel(InputForm form) {
|
||||||
|
this.inputForm = form;
|
||||||
|
}
|
||||||
|
}
|
27
src/main/java/com/craigraw/sparrow/form/InputForm.java
Normal file
27
src/main/java/com/craigraw/sparrow/form/InputForm.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package com.craigraw.sparrow.form;
|
||||||
|
|
||||||
|
import com.craigraw.drongo.protocol.TransactionInput;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class InputForm extends Form {
|
||||||
|
private TransactionInput transactionInput;
|
||||||
|
|
||||||
|
public InputForm(TransactionInput transactionInput) {
|
||||||
|
this.transactionInput = transactionInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getContents() throws IOException {
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("input.fxml"));
|
||||||
|
Node node = loader.load();
|
||||||
|
InputController controller = loader.getController();
|
||||||
|
controller.setModel(this);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "Input #" + transactionInput.getIndex();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.craigraw.sparrow.form;
|
||||||
|
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class InputsController implements Initializable {
|
||||||
|
private InputsForm inputsForm;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModel(InputsForm form) {
|
||||||
|
this.inputsForm = form;
|
||||||
|
}
|
||||||
|
}
|
27
src/main/java/com/craigraw/sparrow/form/InputsForm.java
Normal file
27
src/main/java/com/craigraw/sparrow/form/InputsForm.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package com.craigraw.sparrow.form;
|
||||||
|
|
||||||
|
import com.craigraw.drongo.protocol.Transaction;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class InputsForm extends Form {
|
||||||
|
private Transaction transaction;
|
||||||
|
|
||||||
|
public InputsForm(Transaction transaction) {
|
||||||
|
this.transaction = transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getContents() throws IOException {
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("inputs.fxml"));
|
||||||
|
Node node = loader.load();
|
||||||
|
InputsController controller = loader.getController();
|
||||||
|
controller.setModel(this);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "Inputs";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.craigraw.sparrow.form;
|
||||||
|
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class OutputController implements Initializable {
|
||||||
|
private OutputForm outputForm;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModel(OutputForm form) {
|
||||||
|
this.outputForm = form;
|
||||||
|
}
|
||||||
|
}
|
27
src/main/java/com/craigraw/sparrow/form/OutputForm.java
Normal file
27
src/main/java/com/craigraw/sparrow/form/OutputForm.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package com.craigraw.sparrow.form;
|
||||||
|
|
||||||
|
import com.craigraw.drongo.protocol.TransactionOutput;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class OutputForm extends Form {
|
||||||
|
private TransactionOutput transactionOutput;
|
||||||
|
|
||||||
|
public OutputForm(TransactionOutput transactionOutput) {
|
||||||
|
this.transactionOutput = transactionOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getContents() throws IOException {
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("output.fxml"));
|
||||||
|
Node node = loader.load();
|
||||||
|
OutputController controller = loader.getController();
|
||||||
|
controller.setModel(this);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "Output #" + transactionOutput.getIndex();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.craigraw.sparrow.form;
|
||||||
|
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class OutputsController implements Initializable {
|
||||||
|
private OutputsForm outputsForm;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModel(OutputsForm form) {
|
||||||
|
this.outputsForm = form;
|
||||||
|
}
|
||||||
|
}
|
27
src/main/java/com/craigraw/sparrow/form/OutputsForm.java
Normal file
27
src/main/java/com/craigraw/sparrow/form/OutputsForm.java
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package com.craigraw.sparrow.form;
|
||||||
|
|
||||||
|
import com.craigraw.drongo.protocol.Transaction;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class OutputsForm extends Form {
|
||||||
|
private Transaction transaction;
|
||||||
|
|
||||||
|
public OutputsForm(Transaction transaction) {
|
||||||
|
this.transaction = transaction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getContents() throws IOException {
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("outputs.fxml"));
|
||||||
|
Node node = loader.load();
|
||||||
|
OutputsController controller = loader.getController();
|
||||||
|
controller.setModel(this);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "Outputs";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.craigraw.sparrow.form;
|
||||||
|
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class PartialInputController implements Initializable {
|
||||||
|
private PartialInputForm partialInputForm;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModel(PartialInputForm form) {
|
||||||
|
this.partialInputForm = form;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.craigraw.sparrow.form;
|
||||||
|
|
||||||
|
import com.craigraw.drongo.psbt.PSBTInput;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class PartialInputForm extends Form {
|
||||||
|
private PSBTInput psbtInput;
|
||||||
|
|
||||||
|
public PartialInputForm(PSBTInput psbtInput) {
|
||||||
|
this.psbtInput = psbtInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getContents() throws IOException {
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("partialinput.fxml"));
|
||||||
|
Node node = loader.load();
|
||||||
|
PartialInputController controller = loader.getController();
|
||||||
|
controller.setModel(this);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.craigraw.sparrow.form;
|
||||||
|
|
||||||
|
import javafx.fxml.Initializable;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class PartialOutputController implements Initializable {
|
||||||
|
private PartialOutputForm partialOutputForm;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL location, ResourceBundle resources) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setModel(PartialOutputForm form) {
|
||||||
|
this.partialOutputForm = form;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.craigraw.sparrow.form;
|
||||||
|
|
||||||
|
import com.craigraw.drongo.psbt.PSBTOutput;
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class PartialOutputForm extends Form {
|
||||||
|
private PSBTOutput psbtOutput;
|
||||||
|
|
||||||
|
public PartialOutputForm(PSBTOutput psbtOutput) {
|
||||||
|
this.psbtOutput = psbtOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getContents() throws IOException {
|
||||||
|
FXMLLoader loader = new FXMLLoader(getClass().getResource("partialoutput.fxml"));
|
||||||
|
Node node = loader.load();
|
||||||
|
PartialOutputController controller = loader.getController();
|
||||||
|
controller.setModel(this);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" fx:controller="com.craigraw.sparrow.AppController" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
|
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="700.0" prefWidth="900.0" fx:controller="com.craigraw.sparrow.AppController" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
|
||||||
<children>
|
<children>
|
||||||
<MenuBar>
|
<MenuBar>
|
||||||
<menus>
|
<menus>
|
||||||
|
|
82
src/main/resources/com/craigraw/sparrow/form/headers.fxml
Normal file
82
src/main/resources/com/craigraw/sparrow/form/headers.fxml
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import javafx.scene.text.*?>
|
||||||
|
<?import javafx.scene.control.Label?>
|
||||||
|
<?import javafx.scene.control.TextField?>
|
||||||
|
<?import javafx.scene.control.Spinner?>
|
||||||
|
<?import javafx.scene.control.ToggleButton?>
|
||||||
|
<?import javafx.scene.control.ToggleGroup?>
|
||||||
|
<?import javafx.scene.control.Separator?>
|
||||||
|
<?import tornadofx.control.DateTimePicker?>
|
||||||
|
|
||||||
|
<GridPane fx:id="layout" hgap="10.0" prefHeight="350.0" prefWidth="600.0" vgap="10.0" alignment="CENTER" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.craigraw.sparrow.form.HeadersController" stylesheets="@../general.css">
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="25.0" left="25.0" right="25.0" top="25.0" />
|
||||||
|
</padding>
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints minWidth="100" prefWidth="100"
|
||||||
|
maxWidth="Infinity" halignment="RIGHT">
|
||||||
|
</ColumnConstraints>
|
||||||
|
<ColumnConstraints minWidth="200" prefWidth="200"
|
||||||
|
maxWidth="Infinity" hgrow="ALWAYS">
|
||||||
|
</ColumnConstraints>
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints />
|
||||||
|
</rowConstraints>
|
||||||
|
<!-- Add Header Label -->
|
||||||
|
<Label text="Headers" GridPane.columnIndex="0"
|
||||||
|
GridPane.rowIndex="0" GridPane.columnSpan="2"
|
||||||
|
GridPane.rowSpan="1" GridPane.halignment="LEFT" >
|
||||||
|
<font>
|
||||||
|
<Font name="Arial" size="24" ></Font>
|
||||||
|
</font>
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets top="0" right="0" bottom="10" left="0"></Insets>
|
||||||
|
</GridPane.margin>
|
||||||
|
</Label>
|
||||||
|
|
||||||
|
<Label text="Transaction ID: " GridPane.columnIndex="0" GridPane.rowIndex="1" />
|
||||||
|
<TextField fx:id="idField" editable="false" styleClass="copyable-label" prefHeight="40" prefWidth="520" minWidth="520" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
|
||||||
|
|
||||||
|
<Label text="Version: " GridPane.columnIndex="0" GridPane.rowIndex="2" />
|
||||||
|
<Spinner fx:id="versionField" prefHeight="40" prefWidth="60" editable="true" GridPane.columnIndex="1" GridPane.rowIndex="2" />
|
||||||
|
|
||||||
|
<Label text="Type: " GridPane.columnIndex="0" GridPane.rowIndex="3" />
|
||||||
|
<Label fx:id="segwitField" prefHeight="40" GridPane.columnIndex="1" GridPane.rowIndex="3"/>
|
||||||
|
|
||||||
|
<Label fx:id="segwitVersionLabel" text="Segwit Version: " GridPane.columnIndex="0" GridPane.rowIndex="4" />
|
||||||
|
<Spinner fx:id="segwitVersionField" prefWidth="60" prefHeight="40" GridPane.columnIndex="1" GridPane.rowIndex="4"/>
|
||||||
|
|
||||||
|
<Separator GridPane.columnIndex="0" GridPane.rowIndex="5" GridPane.columnSpan="2" GridPane.rowSpan="1" GridPane.halignment="LEFT" />
|
||||||
|
|
||||||
|
<Label text="Locktime Type: " GridPane.columnIndex="0" GridPane.rowIndex="6" />
|
||||||
|
<HBox fillHeight="true" GridPane.columnIndex="1" GridPane.rowIndex="6">
|
||||||
|
<children>
|
||||||
|
<ToggleButton fx:id="locktimeBlockTypeField" text="Block" userData="block">
|
||||||
|
<toggleGroup>
|
||||||
|
<ToggleGroup fx:id="locktimeTypeField" />
|
||||||
|
</toggleGroup>
|
||||||
|
</ToggleButton>
|
||||||
|
<ToggleButton fx:id="locktimeDateTypeField" text="Date" toggleGroup="$locktimeTypeField" userData="date" />
|
||||||
|
</children>
|
||||||
|
</HBox>
|
||||||
|
|
||||||
|
<Label text="Locktime: " GridPane.columnIndex="0" GridPane.rowIndex="7" />
|
||||||
|
<Spinner fx:id="locktimeBlockField" prefHeight="40" prefWidth="120" GridPane.columnIndex="1" GridPane.rowIndex="7"/>
|
||||||
|
<DateTimePicker fx:id="locktimeDateField" visible="false" prefHeight="40" GridPane.columnIndex="1" GridPane.rowIndex="7"/>
|
||||||
|
|
||||||
|
<Separator GridPane.columnIndex="0" GridPane.rowIndex="8" GridPane.columnSpan="2" GridPane.rowSpan="1" GridPane.halignment="LEFT" />
|
||||||
|
|
||||||
|
<Label text="Size: " GridPane.columnIndex="0" GridPane.rowIndex="9" />
|
||||||
|
<Label fx:id="sizeField" prefHeight="40" prefWidth="120" GridPane.columnIndex="1" GridPane.rowIndex="9"/>
|
||||||
|
|
||||||
|
<Label text="Virtual Size: " GridPane.columnIndex="0" GridPane.rowIndex="10" />
|
||||||
|
<Label fx:id="virtualSizeField" prefHeight="40" prefWidth="120" GridPane.columnIndex="1" GridPane.rowIndex="10"/>
|
||||||
|
|
||||||
|
<Label text="Fee: " GridPane.columnIndex="0" GridPane.rowIndex="11" />
|
||||||
|
<Label fx:id="feeField" prefHeight="40" prefWidth="120" GridPane.columnIndex="1" GridPane.rowIndex="11"/>
|
||||||
|
|
||||||
|
</GridPane>
|
14
src/main/resources/com/craigraw/sparrow/form/input.fxml
Normal file
14
src/main/resources/com/craigraw/sparrow/form/input.fxml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import java.util.*?>
|
||||||
|
<?import javafx.scene.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<AnchorPane xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="com.craigraw.sparrow.form.InputController"
|
||||||
|
prefHeight="400.0" prefWidth="600.0">
|
||||||
|
|
||||||
|
</AnchorPane>
|
14
src/main/resources/com/craigraw/sparrow/form/inputs.fxml
Normal file
14
src/main/resources/com/craigraw/sparrow/form/inputs.fxml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import java.util.*?>
|
||||||
|
<?import javafx.scene.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<AnchorPane xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="com.craigraw.sparrow.form.InputsController"
|
||||||
|
prefHeight="400.0" prefWidth="600.0">
|
||||||
|
|
||||||
|
</AnchorPane>
|
14
src/main/resources/com/craigraw/sparrow/form/output.fxml
Normal file
14
src/main/resources/com/craigraw/sparrow/form/output.fxml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import java.util.*?>
|
||||||
|
<?import javafx.scene.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<AnchorPane xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="com.craigraw.sparrow.form.OutputController"
|
||||||
|
prefHeight="400.0" prefWidth="600.0">
|
||||||
|
|
||||||
|
</AnchorPane>
|
14
src/main/resources/com/craigraw/sparrow/form/outputs.fxml
Normal file
14
src/main/resources/com/craigraw/sparrow/form/outputs.fxml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import java.util.*?>
|
||||||
|
<?import javafx.scene.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<AnchorPane xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="com.craigraw.sparrow.form.OutputsController"
|
||||||
|
prefHeight="400.0" prefWidth="600.0">
|
||||||
|
|
||||||
|
</AnchorPane>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import java.util.*?>
|
||||||
|
<?import javafx.scene.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<AnchorPane xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="com.craigraw.sparrow.form.PartialInputController"
|
||||||
|
prefHeight="400.0" prefWidth="600.0">
|
||||||
|
|
||||||
|
</AnchorPane>
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import java.lang.*?>
|
||||||
|
<?import java.util.*?>
|
||||||
|
<?import javafx.scene.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<AnchorPane xmlns="http://javafx.com/javafx"
|
||||||
|
xmlns:fx="http://javafx.com/fxml"
|
||||||
|
fx:controller="com.craigraw.sparrow.form.PartialOutputController"
|
||||||
|
prefHeight="400.0" prefWidth="600.0">
|
||||||
|
|
||||||
|
</AnchorPane>
|
6
src/main/resources/com/craigraw/sparrow/general.css
Normal file
6
src/main/resources/com/craigraw/sparrow/general.css
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
.copyable-label, .copyable-label:focused {
|
||||||
|
-fx-background-color: transparent ;
|
||||||
|
-fx-background-insets: 0px ;
|
||||||
|
-fx-border :none;
|
||||||
|
-fx-padding: 0;
|
||||||
|
}
|
27
src/main/resources/com/craigraw/sparrow/transaction.css
Normal file
27
src/main/resources/com/craigraw/sparrow/transaction.css
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#txhex {
|
||||||
|
-fx-background-color: #ffffff;
|
||||||
|
-fx-font: 14px Courier;
|
||||||
|
-fx-padding: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-color { -fx-fill: #986801 }
|
||||||
|
.segwit-marker-color { -fx-fill: #000000 }
|
||||||
|
.segwit-flag-color { -fx-fill: #4078f2 }
|
||||||
|
.num-inputs-color { -fx-fill: #ca1243 }
|
||||||
|
.inputs-color { -fx-fill: #0184bc }
|
||||||
|
.num-outputs-color { -fx-fill: #ca1243 }
|
||||||
|
.outputs-color { -fx-fill: #50a14f }
|
||||||
|
.witnesses-color { -fx-fill: #a626a4 }
|
||||||
|
.locktime-color { -fx-fill: #986801 }
|
||||||
|
|
||||||
|
.color-0 { -fx-fill: #ca1243 }
|
||||||
|
.color-1 { -fx-fill: #d75f00 }
|
||||||
|
.color-2 { -fx-fill: #c18401 }
|
||||||
|
.color-3 { -fx-fill: #50a14f }
|
||||||
|
.color-4 { -fx-fill: #0184bc }
|
||||||
|
.color-5 { -fx-fill: #4078f2 }
|
||||||
|
.color-6 { -fx-fill: #a626a4 }
|
||||||
|
.color-7 { -fx-fill: #986801 }
|
||||||
|
.color-8 { -fx-fill: #000000 }
|
||||||
|
|
||||||
|
.color-grey { -fx-fill: #e5e5e6 }
|
|
@ -6,23 +6,16 @@
|
||||||
<?import javafx.scene.control.*?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.layout.*?>
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
<SplitPane dividerPositions="0.7" orientation="VERTICAL" prefHeight="200.0" prefWidth="160.0" VBox.vgrow="ALWAYS" fx:controller="com.craigraw.sparrow.TransactionController" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
|
<?import javafx.scene.text.TextFlow?>
|
||||||
|
<?import org.fxmisc.richtext.CodeArea?>
|
||||||
|
<SplitPane dividerPositions="0.7" orientation="VERTICAL" prefHeight="200.0" prefWidth="160.0" VBox.vgrow="ALWAYS" fx:controller="com.craigraw.sparrow.TransactionController" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" stylesheets="@transaction.css">
|
||||||
<items>
|
<items>
|
||||||
<SplitPane dividerPositions="0.4" prefHeight="160.0" prefWidth="200.0">
|
<SplitPane dividerPositions="0.2" prefHeight="160.0" prefWidth="200.0">
|
||||||
<items>
|
<items>
|
||||||
<TreeView prefHeight="154.0" prefWidth="174.0" fx:id="txtree" />
|
<TreeView prefHeight="154.0" prefWidth="174.0" fx:id="txtree" />
|
||||||
<ScrollPane fitToHeight="true" fitToWidth="true" prefHeight="200.0" prefWidth="200.0">
|
<Pane fx:id="txpane" />
|
||||||
<content>
|
|
||||||
<TableView prefHeight="200.0" prefWidth="200.0" fx:id="txnode">
|
|
||||||
<columns>
|
|
||||||
<TableColumn prefWidth="75.0" text="Name" />
|
|
||||||
<TableColumn prefWidth="300.0" text="Value" />
|
|
||||||
</columns>
|
|
||||||
</TableView>
|
|
||||||
</content>
|
|
||||||
</ScrollPane>
|
|
||||||
</items>
|
</items>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
<TextArea />
|
<CodeArea fx:id="txhex" wrapText="true" editable="false" />
|
||||||
</items>
|
</items>
|
||||||
</SplitPane>
|
</SplitPane>
|
Loading…
Reference in a new issue