diff --git a/drongo b/drongo index 6b20c655..deb45687 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit 6b20c6558ab7cef6f582461692232a7687fe26c8 +Subproject commit deb45687c08d4ff824ab7559233249c345d9c86a diff --git a/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java b/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java index 3efbaf2d..7e3b6196 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/EntryCell.java @@ -2,7 +2,6 @@ package com.sparrowwallet.sparrow.control; import com.sparrowwallet.drongo.Utils; import com.sparrowwallet.drongo.address.Address; -import com.sparrowwallet.drongo.protocol.NonStandardScriptException; import com.sparrowwallet.drongo.protocol.Transaction; import com.sparrowwallet.drongo.protocol.TransactionOutput; import com.sparrowwallet.drongo.wallet.*; @@ -125,7 +124,7 @@ class EntryCell extends TreeTableCell { } else if(entry instanceof HashIndexEntry) { HashIndexEntry hashIndexEntry = (HashIndexEntry)entry; setText(hashIndexEntry.getDescription()); - setContextMenu(new HashIndexEntryContextMenu(hashIndexEntry)); + setContextMenu(new HashIndexEntryContextMenu(getTreeTableView(), hashIndexEntry)); Tooltip tooltip = new Tooltip(); tooltip.setText(hashIndexEntry.getHashIndex().toString()); setTooltip(tooltip); @@ -140,26 +139,13 @@ class EntryCell extends TreeTableCell { }); actionBox.getChildren().add(viewTransactionButton); - if(hashIndexEntry.getType().equals(HashIndexEntry.Type.OUTPUT) && hashIndexEntry.isSpendable()) { + if(hashIndexEntry.getType().equals(HashIndexEntry.Type.OUTPUT) && hashIndexEntry.isSpendable() && !hashIndexEntry.getHashIndex().isSpent()) { Button spendUtxoButton = new Button(""); Glyph sendGlyph = new Glyph("FontAwesome", FontAwesome.Glyph.SEND); sendGlyph.setFontSize(12); spendUtxoButton.setGraphic(sendGlyph); spendUtxoButton.setOnAction(event -> { - List utxoEntries = getTreeTableView().getSelectionModel().getSelectedCells().stream() - .map(tp -> tp.getTreeItem().getValue()) - .filter(e -> e instanceof HashIndexEntry) - .map(e -> (HashIndexEntry)e) - .filter(e -> e.getType().equals(HashIndexEntry.Type.OUTPUT) && e.isSpendable()) - .collect(Collectors.toList()); - - if(!utxoEntries.contains(hashIndexEntry)) { - utxoEntries = List.of(hashIndexEntry); - } - - final List spendingUtxos = utxoEntries.stream().map(HashIndexEntry::getHashIndex).collect(Collectors.toList()); - EventManager.get().post(new SendActionEvent(hashIndexEntry.getWallet(), spendingUtxos)); - Platform.runLater(() -> EventManager.get().post(new SpendUtxoEvent(hashIndexEntry.getWallet(), spendingUtxos))); + sendSelectedUtxos(getTreeTableView(), hashIndexEntry); }); actionBox.getChildren().add(spendUtxoButton); } @@ -223,6 +209,33 @@ class EntryCell extends TreeTableCell { return AppController.getTargetBlockFeeRates().values().iterator().next(); } + private static void sendSelectedUtxos(TreeTableView treeTableView, HashIndexEntry hashIndexEntry) { + List utxoEntries = treeTableView.getSelectionModel().getSelectedCells().stream() + .map(tp -> tp.getTreeItem().getValue()) + .filter(e -> e instanceof HashIndexEntry) + .map(e -> (HashIndexEntry)e) + .filter(e -> e.getType().equals(HashIndexEntry.Type.OUTPUT) && e.isSpendable()) + .collect(Collectors.toList()); + + if(!utxoEntries.contains(hashIndexEntry)) { + utxoEntries = List.of(hashIndexEntry); + } + + final List spendingUtxos = utxoEntries.stream().map(HashIndexEntry::getHashIndex).collect(Collectors.toList()); + EventManager.get().post(new SendActionEvent(hashIndexEntry.getWallet(), spendingUtxos)); + Platform.runLater(() -> EventManager.get().post(new SpendUtxoEvent(hashIndexEntry.getWallet(), spendingUtxos))); + } + + private static void freezeUtxo(HashIndexEntry hashIndexEntry) { + hashIndexEntry.getHashIndex().setStatus(Status.FROZEN); + EventManager.get().post(new WalletUtxoStatusChangedEvent(hashIndexEntry.getWallet(), hashIndexEntry.getHashIndex())); + } + + private static void unfreezeUtxo(HashIndexEntry hashIndexEntry) { + hashIndexEntry.getHashIndex().setStatus(null); + EventManager.get().post(new WalletUtxoStatusChangedEvent(hashIndexEntry.getWallet(), hashIndexEntry.getHashIndex())); + } + private static class UnconfirmedTransactionContextMenu extends ContextMenu { public UnconfirmedTransactionContextMenu(TransactionEntry transactionEntry) { BlockTransaction blockTransaction = transactionEntry.getBlockTransaction(); @@ -320,7 +333,7 @@ class EntryCell extends TreeTableCell { } private static class HashIndexEntryContextMenu extends ContextMenu { - public HashIndexEntryContextMenu(HashIndexEntry hashIndexEntry) { + public HashIndexEntryContextMenu(TreeTableView treeTableView, HashIndexEntry hashIndexEntry) { String label = "Copy " + (hashIndexEntry.getType().equals(HashIndexEntry.Type.OUTPUT) ? "Transaction Output" : "Transaction Input"); MenuItem copyHashIndex = new MenuItem(label); copyHashIndex.setOnAction(AE -> { @@ -329,8 +342,34 @@ class EntryCell extends TreeTableCell { content.putString(hashIndexEntry.getHashIndex().toString()); Clipboard.getSystemClipboard().setContent(content); }); - getItems().add(copyHashIndex); + + if(hashIndexEntry.getType().equals(HashIndexEntry.Type.OUTPUT) && hashIndexEntry.isSpendable() && !hashIndexEntry.getHashIndex().isSpent()) { + MenuItem sendSelected = new MenuItem("Send Selected"); + sendSelected.setOnAction(AE -> { + hide(); + sendSelectedUtxos(treeTableView, hashIndexEntry); + }); + getItems().add(sendSelected); + } + + if(hashIndexEntry.getType().equals(HashIndexEntry.Type.OUTPUT) && !hashIndexEntry.getHashIndex().isSpent()) { + if(hashIndexEntry.getHashIndex().getStatus() == null || hashIndexEntry.getHashIndex().getStatus() != Status.FROZEN) { + MenuItem freezeUtxo = new MenuItem("Freeze UTXO"); + freezeUtxo.setOnAction(AE -> { + hide(); + freezeUtxo(hashIndexEntry); + }); + getItems().add(freezeUtxo); + } else { + MenuItem unfreezeUtxo = new MenuItem("Unfreeze UTXO"); + unfreezeUtxo.setOnAction(AE -> { + hide(); + unfreezeUtxo(hashIndexEntry); + }); + getItems().add(unfreezeUtxo); + } + } } } diff --git a/src/main/java/com/sparrowwallet/sparrow/event/WalletUtxoStatusChangedEvent.java b/src/main/java/com/sparrowwallet/sparrow/event/WalletUtxoStatusChangedEvent.java new file mode 100644 index 00000000..2eb73564 --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/event/WalletUtxoStatusChangedEvent.java @@ -0,0 +1,17 @@ +package com.sparrowwallet.sparrow.event; + +import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex; +import com.sparrowwallet.drongo.wallet.Wallet; + +public class WalletUtxoStatusChangedEvent extends WalletDataChangedEvent { + private final BlockTransactionHashIndex utxo; + + public WalletUtxoStatusChangedEvent(Wallet wallet, BlockTransactionHashIndex utxo) { + super(wallet); + this.utxo = utxo; + } + + public BlockTransactionHashIndex getUtxo() { + return utxo; + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/AddressesController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/AddressesController.java index e0e03a9b..92d9cfbb 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/AddressesController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/AddressesController.java @@ -67,4 +67,12 @@ public class AddressesController extends WalletFormController implements Initial receiveTable.setBitcoinUnit(getWalletForm().getWallet(), event.getBitcoinUnit()); changeTable.setBitcoinUnit(getWalletForm().getWallet(), event.getBitcoinUnit()); } + + @Subscribe + public void walletUtxoStatusChanged(WalletUtxoStatusChangedEvent event) { + if(event.getWallet().equals(getWalletForm().getWallet())) { + receiveTable.refresh(); + changeTable.refresh(); + } + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/HashIndexEntry.java b/src/main/java/com/sparrowwallet/sparrow/wallet/HashIndexEntry.java index 2c0cafcc..773f7351 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/HashIndexEntry.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/HashIndexEntry.java @@ -3,6 +3,7 @@ package com.sparrowwallet.sparrow.wallet; import com.sparrowwallet.drongo.KeyPurpose; import com.sparrowwallet.drongo.wallet.BlockTransaction; import com.sparrowwallet.drongo.wallet.BlockTransactionHashIndex; +import com.sparrowwallet.drongo.wallet.Status; import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.control.DateLabel; @@ -57,7 +58,7 @@ public class HashIndexEntry extends Entry implements Comparable } public boolean isSpendable() { - return !isSpent() && (hashIndex.getHeight() > 0 || getWallet().allInputsFromWallet(hashIndex.getHash())); + return !isSpent() && (hashIndex.getHeight() > 0 || getWallet().allInputsFromWallet(hashIndex.getHash())) && (hashIndex.getStatus() == null || hashIndex.getStatus() != Status.FROZEN); } @Override diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java index 4bcb153e..579335b1 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/SendController.java @@ -485,10 +485,10 @@ public class SendController extends WalletFormController implements Initializabl private List getUtxoFilters() { UtxoFilter utxoFilter = utxoFilterProperty.get(); if(utxoFilter != null) { - return List.of(utxoFilter); + return List.of(utxoFilter, new FrozenUtxoFilter()); } - return Collections.emptyList(); + return List.of(new FrozenUtxoFilter()); } private void updateFeeRateSelection(FeeRatesSelection feeRatesSelection) { @@ -877,4 +877,21 @@ public class SendController extends WalletFormController implements Initializabl } } } + + @Subscribe + public void walletUtxoStatusChanged(WalletUtxoStatusChangedEvent event) { + if(event.getWallet().equals(getWalletForm().getWallet())) { + UtxoSelector utxoSelector = utxoSelectorProperty.get(); + if(utxoSelector instanceof MaxUtxoSelector) { + updateTransaction(true); + } else if(utxoSelectorProperty().get() instanceof PresetUtxoSelector) { + PresetUtxoSelector presetUtxoSelector = new PresetUtxoSelector(((PresetUtxoSelector)utxoSelector).getPresetUtxos()); + presetUtxoSelector.getPresetUtxos().remove(event.getUtxo()); + utxoSelectorProperty.set(presetUtxoSelector); + updateTransaction(true); + } else { + updateTransaction(); + } + } + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionsController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionsController.java index 092a39e0..d22ba682 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionsController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/TransactionsController.java @@ -127,4 +127,11 @@ public class TransactionsController extends WalletFormController implements Init public void walletHistoryStatus(WalletHistoryStatusEvent event) { transactionsTable.updateHistoryStatus(event); } + + @Subscribe + public void walletUtxoStatusChanged(WalletUtxoStatusChangedEvent event) { + if(event.getWallet().equals(getWalletForm().getWallet())) { + transactionsTable.refresh(); + } + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/UtxosController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/UtxosController.java index fe8c7626..2da0b76e 100644 --- a/src/main/java/com/sparrowwallet/sparrow/wallet/UtxosController.java +++ b/src/main/java/com/sparrowwallet/sparrow/wallet/UtxosController.java @@ -132,4 +132,11 @@ public class UtxosController extends WalletFormController implements Initializab public void walletHistoryStatus(WalletHistoryStatusEvent event) { utxosTable.updateHistoryStatus(event); } + + @Subscribe + public void walletUtxoStatusChanged(WalletUtxoStatusChangedEvent event) { + if(event.getWallet().equals(getWalletForm().getWallet())) { + utxosTable.refresh(); + } + } }