diff --git a/src/main/java/com/sparrowwallet/sparrow/AppController.java b/src/main/java/com/sparrowwallet/sparrow/AppController.java index 7a0b2d2f..2eea39d7 100644 --- a/src/main/java/com/sparrowwallet/sparrow/AppController.java +++ b/src/main/java/com/sparrowwallet/sparrow/AppController.java @@ -176,6 +176,7 @@ public class AppController implements Initializable { openTransactionIdItem.disableProperty().bind(onlineProperty.not()); openWalletFile(new File("/Users/scy/.sparrow/wallets/sparta.json")); + openWalletFile(new File("/Users/scy/.sparrow/wallets/sparta-test.json")); } private ElectrumServer.ConnectionService createConnectionService() { diff --git a/src/main/java/com/sparrowwallet/sparrow/control/AddressTreeTable.java b/src/main/java/com/sparrowwallet/sparrow/control/AddressTreeTable.java index ed16809f..055002a6 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/AddressTreeTable.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/AddressTreeTable.java @@ -43,8 +43,8 @@ public class AddressTreeTable extends TreeTableView { labelCol.setSortable(false); getColumns().add(labelCol); - TreeTableColumn amountCol = new TreeTableColumn<>("Value"); - amountCol.setCellValueFactory((TreeTableColumn.CellDataFeatures param) -> { + TreeTableColumn amountCol = new TreeTableColumn<>("Value"); + amountCol.setCellValueFactory((TreeTableColumn.CellDataFeatures param) -> { return new ReadOnlyObjectWrapper<>(param.getValue().getValue().getValue()); }); amountCol.setCellFactory(p -> new AmountCell()); @@ -54,6 +54,9 @@ public class AddressTreeTable extends TreeTableView { setEditable(true); setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY); + addressCol.setSortType(TreeTableColumn.SortType.ASCENDING); + getSortOrder().add(addressCol); + Integer highestUsedIndex = rootEntry.getNode().getHighestUsedIndex(); if(highestUsedIndex != null) { scrollTo(highestUsedIndex); @@ -80,18 +83,29 @@ public class AddressTreeTable extends TreeTableView { RecursiveTreeItem rootItem = new RecursiveTreeItem<>(rootEntry, Entry::getChildren); setRoot(rootItem); rootItem.setExpanded(true); + + if(getColumns().size() > 0 && getSortOrder().isEmpty()) { + TreeTableColumn addressCol = getColumns().get(0); + getSortOrder().add(addressCol); + addressCol.setSortType(TreeTableColumn.SortType.ASCENDING); + } } public void updateHistory(List updatedNodes) { NodeEntry rootEntry = (NodeEntry)getRoot().getValue(); for(WalletNode updatedNode : updatedNodes) { + NodeEntry nodeEntry = new NodeEntry(rootEntry.getWallet(), updatedNode); + Optional optEntry = rootEntry.getChildren().stream().filter(childEntry -> ((NodeEntry)childEntry).getNode().equals(updatedNode)).findFirst(); if(optEntry.isPresent()) { int index = rootEntry.getChildren().indexOf(optEntry.get()); - NodeEntry nodeEntry = new NodeEntry(rootEntry.getWallet(), updatedNode); rootEntry.getChildren().set(index, nodeEntry); + } else { + rootEntry.getChildren().add(nodeEntry); } } + + sort(); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/AmountCell.java b/src/main/java/com/sparrowwallet/sparrow/control/AmountCell.java index 67b374ae..7c57691b 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/AmountCell.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/AmountCell.java @@ -11,14 +11,14 @@ import javafx.scene.layout.Region; import java.util.Locale; -class AmountCell extends TreeTableCell { +class AmountCell extends TreeTableCell { public AmountCell() { super(); getStyleClass().add("amount-cell"); } @Override - protected void updateItem(Long amount, boolean empty) { + protected void updateItem(Number amount, boolean empty) { super.updateItem(amount, empty); if(empty || amount == null) { @@ -28,7 +28,7 @@ class AmountCell extends TreeTableCell { Entry entry = getTreeTableView().getTreeItem(getIndex()).getValue(); EntryCell.applyRowStyles(this, entry); - String satsValue = String.format(Locale.ENGLISH, "%,d", amount); + String satsValue = String.format(Locale.ENGLISH, "%,d", amount.longValue()); final String btcValue = CoinLabel.getBTCFormat().format(amount.doubleValue() / Transaction.SATOSHIS_PER_BITCOIN) + " BTC"; if(entry instanceof TransactionEntry) { @@ -71,7 +71,6 @@ class AmountCell extends TreeTableCell { } setText(satsValue); - } } } diff --git a/src/main/java/com/sparrowwallet/sparrow/control/RecursiveTreeItem.java b/src/main/java/com/sparrowwallet/sparrow/control/RecursiveTreeItem.java index 600cf1b5..2a41de98 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/RecursiveTreeItem.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/RecursiveTreeItem.java @@ -50,7 +50,11 @@ public class RecursiveTreeItem extends TreeItem { while(change.next()){ if(change.wasAdded()){ - change.getAddedSubList().forEach(t-> RecursiveTreeItem.this.getChildren().add(change.getFrom(), new RecursiveTreeItem<>(t, this.graphicsFactory, childrenFactory))); + if(change.getFrom() >= RecursiveTreeItem.this.getChildren().size()) { + change.getAddedSubList().forEach(t-> RecursiveTreeItem.this.getChildren().add(new RecursiveTreeItem<>(t, this.graphicsFactory, childrenFactory))); + } else { + change.getAddedSubList().forEach(t-> RecursiveTreeItem.this.getChildren().add(change.getFrom(), new RecursiveTreeItem<>(t, this.graphicsFactory, childrenFactory))); + } } if(change.wasRemoved()){ diff --git a/src/main/java/com/sparrowwallet/sparrow/control/TransactionsTreeTable.java b/src/main/java/com/sparrowwallet/sparrow/control/TransactionsTreeTable.java index e51d137a..8b9f5109 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/TransactionsTreeTable.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/TransactionsTreeTable.java @@ -34,17 +34,17 @@ public class TransactionsTreeTable extends TreeTableView { labelCol.setSortable(true); getColumns().add(labelCol); - TreeTableColumn amountCol = new TreeTableColumn<>("Value"); - amountCol.setCellValueFactory((TreeTableColumn.CellDataFeatures param) -> { + TreeTableColumn amountCol = new TreeTableColumn<>("Value"); + amountCol.setCellValueFactory((TreeTableColumn.CellDataFeatures param) -> { return new ReadOnlyObjectWrapper<>(param.getValue().getValue().getValue()); }); amountCol.setCellFactory(p -> new AmountCell()); amountCol.setSortable(true); getColumns().add(amountCol); - TreeTableColumn balanceCol = new TreeTableColumn<>("Balance"); - balanceCol.setCellValueFactory((TreeTableColumn.CellDataFeatures param) -> { - return new ReadOnlyObjectWrapper<>(param.getValue().getValue() instanceof TransactionEntry ? ((TransactionEntry)param.getValue().getValue()).getBalance() : null); + TreeTableColumn balanceCol = new TreeTableColumn<>("Balance"); + balanceCol.setCellValueFactory((TreeTableColumn.CellDataFeatures param) -> { + return param.getValue().getValue() instanceof TransactionEntry ? ((TransactionEntry)param.getValue().getValue()).balanceProperty() : new ReadOnlyObjectWrapper<>(null); }); balanceCol.setCellFactory(p -> new AmountCell()); balanceCol.setSortable(true); @@ -62,7 +62,7 @@ public class TransactionsTreeTable extends TreeTableView { setRoot(rootItem); rootItem.setExpanded(true); - if(getColumns().size() > 0) { + if(getColumns().size() > 0 && getSortOrder().isEmpty()) { TreeTableColumn dateCol = getColumns().get(0); getSortOrder().add(dateCol); dateCol.setSortType(TreeTableColumn.SortType.DESCENDING); @@ -72,5 +72,6 @@ public class TransactionsTreeTable extends TreeTableView { public void updateHistory(List updatedNodes) { WalletTransactionsEntry rootEntry = (WalletTransactionsEntry)getRoot().getValue(); rootEntry.updateTransactions(); + sort(); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/Storage.java b/src/main/java/com/sparrowwallet/sparrow/io/Storage.java index eece215b..cb694359 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/Storage.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/Storage.java @@ -366,7 +366,10 @@ public class Storage { @Override public WalletNode deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { WalletNode node = getGson(false).fromJson(json, typeOfT); + node.parseDerivation(); + for(WalletNode childNode : node.getChildren()) { + childNode.parseDerivation(); if(childNode.getChildren() == null) { childNode.setChildren(new TreeSet<>()); } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/Entry.java b/src/main/java/com/sparrowwallet/sparrow/wallet/Entry.java index 888b5d33..1bbae905 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/Entry.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/Entry.java @@ -11,7 +11,7 @@ public abstract class Entry { private final ObservableList children; public Entry(String label, List entries) { - this.labelProperty = new SimpleStringProperty(label); + this.labelProperty = new SimpleStringProperty(this, "label", label); this.children = FXCollections.observableList(entries); } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/NodeEntry.java b/src/main/java/com/sparrowwallet/sparrow/wallet/NodeEntry.java index c62641ef..9890d731 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/NodeEntry.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/NodeEntry.java @@ -9,7 +9,7 @@ import com.sparrowwallet.sparrow.event.WalletEntryLabelChangedEvent; import java.util.stream.Collectors; -public class NodeEntry extends Entry { +public class NodeEntry extends Entry implements Comparable { private final Wallet wallet; private final WalletNode node; @@ -56,4 +56,9 @@ public class NodeEntry extends Entry { return node.getUnspentValue(); } + + @Override + public int compareTo(NodeEntry other) { + return node.compareTo(other.node); + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionEntry.java b/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionEntry.java index fbd5fb81..550fb4cd 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionEntry.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionEntry.java @@ -10,6 +10,8 @@ import com.sparrowwallet.sparrow.event.WalletBlockHeightChangedEvent; import com.sparrowwallet.sparrow.event.WalletEntryLabelChangedEvent; import javafx.beans.property.IntegerProperty; import javafx.beans.property.IntegerPropertyBase; +import javafx.beans.property.LongProperty; +import javafx.beans.property.LongPropertyBase; import java.util.*; import java.util.stream.Collectors; @@ -20,7 +22,6 @@ public class TransactionEntry extends Entry implements Comparable inputs, Map outputs) { super(blockTransaction.getLabel(), createChildEntries(wallet, inputs, outputs)); @@ -42,10 +43,6 @@ public class TransactionEntry extends Entry implements Comparable ((TransactionEntry)entry).setParent(this)); + calculateBalances(); } @Override public Long getValue() { - return getBalance(null); + return getBalance(); } - protected Long getBalance(TransactionEntry transactionEntry) { + protected void calculateBalances() { long balance = 0L; - for(Entry entry : getChildren()) { - balance += entry.getValue(); - if(entry == transactionEntry) { - return balance; - } + //Note transaction entries must be in ascending order. This sorting is ultimately done according to BlockTransactions' comparator + getChildren().sort(Comparator.comparing(TransactionEntry.class::cast)); + + for(Entry entry : getChildren()) { + TransactionEntry transactionEntry = (TransactionEntry)entry; + balance += entry.getValue(); + transactionEntry.setBalance(balance); } - return balance; + setBalance(balance); } public void updateTransactions() { - List current = getWalletTransactions(wallet).stream().map(WalletTransaction::getTransactionEntry).peek(entry -> entry.setParent(this)).collect(Collectors.toList()); + List current = getWalletTransactions(wallet).stream().map(WalletTransaction::getTransactionEntry).collect(Collectors.toList()); List previous = new ArrayList<>(getChildren()); - for(Entry currentEntry : current) { - int index = previous.indexOf(currentEntry); - if (index > -1) { - getChildren().set(index, currentEntry); - } else { - getChildren().add(currentEntry); - } - } - getChildren().sort(Comparator.comparing(TransactionEntry.class::cast)); + List entriesAdded = new ArrayList<>(current); + entriesAdded.removeAll(previous); + getChildren().addAll(entriesAdded); + + List entriesRemoved = new ArrayList<>(previous); + entriesRemoved.removeAll(current); + getChildren().removeAll(entriesRemoved); + + calculateBalances(); } private static Collection getWalletTransactions(Wallet wallet) { @@ -57,9 +61,7 @@ public class WalletTransactionsEntry extends Entry { getWalletTransactions(wallet, walletTransactionMap, wallet.getNode(KeyPurpose.RECEIVE)); getWalletTransactions(wallet, walletTransactionMap, wallet.getNode(KeyPurpose.CHANGE)); - List walletTxList = new ArrayList<>(walletTransactionMap.values()); - Collections.reverse(walletTxList); - return walletTxList; + return new ArrayList<>(walletTransactionMap.values()); } private static void getWalletTransactions(Wallet wallet, Map walletTransactionMap, WalletNode purposeNode) { @@ -87,6 +89,39 @@ public class WalletTransactionsEntry extends Entry { } } + /** + * Defines the wallet balance in total. + */ + private LongProperty balance; + + public final void setBalance(long value) { + if(balance != null || value != 0) { + balanceProperty().set(value); + } + } + + public final long getBalance() { + return balance == null ? 0L : balance.get(); + } + + public final LongProperty balanceProperty() { + if(balance == null) { + balance = new LongPropertyBase(0L) { + + @Override + public Object getBean() { + return WalletTransactionsEntry.this; + } + + @Override + public String getName() { + return "balance"; + } + }; + } + return balance; + } + private static class WalletTransaction { private final Wallet wallet; private final BlockTransaction blockTransaction;