From 85eb4df7e929cea944ba1e55c73a04415e34f424 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Wed, 22 Nov 2023 07:59:26 +0200 Subject: [PATCH] transaction tab: rearrange via transaction tree --- drongo | 2 +- .../sparrow/event/PSBTReorderedEvent.java | 15 +++ .../transaction/HeadersController.java | 8 ++ .../transaction/IndexedTransactionForm.java | 6 +- .../sparrow/transaction/InputController.java | 7 ++ .../sparrow/transaction/OutputController.java | 8 ++ .../sparrow/transaction/OutputForm.java | 16 +-- .../transaction/TransactionController.java | 100 ++++++++++++++++++ 8 files changed, 149 insertions(+), 13 deletions(-) create mode 100644 src/main/java/com/sparrowwallet/sparrow/event/PSBTReorderedEvent.java diff --git a/drongo b/drongo index 0815484c..0bb5b75b 160000 --- a/drongo +++ b/drongo @@ -1 +1 @@ -Subproject commit 0815484c4cb384522cf215ef18fc69a666b43c37 +Subproject commit 0bb5b75be52ca4ee9dc771dae30d2b7cb9a49bd2 diff --git a/src/main/java/com/sparrowwallet/sparrow/event/PSBTReorderedEvent.java b/src/main/java/com/sparrowwallet/sparrow/event/PSBTReorderedEvent.java new file mode 100644 index 00000000..a921a2d6 --- /dev/null +++ b/src/main/java/com/sparrowwallet/sparrow/event/PSBTReorderedEvent.java @@ -0,0 +1,15 @@ +package com.sparrowwallet.sparrow.event; + +import com.sparrowwallet.drongo.psbt.PSBT; + +public class PSBTReorderedEvent { + private final PSBT psbt; + + public PSBTReorderedEvent(PSBT psbt) { + this.psbt = psbt; + } + + public PSBT getPsbt() { + return psbt; + } +} diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java index a484281d..9df562a3 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/HeadersController.java @@ -1593,6 +1593,14 @@ public class HeadersController extends TransactionFormController implements Init } } + @Subscribe + public void psbtReordered(PSBTReorderedEvent event) { + if(event.getPsbt().equals(headersForm.getPsbt())) { + updateTxId(); + transactionDiagram.update(getWalletTransaction(headersForm.getInputTransactions())); + } + } + private static class WalletSignComparator implements Comparator { private static final List sourceOrder = List.of(KeystoreSource.SW_WATCH, KeystoreSource.HW_AIRGAPPED, KeystoreSource.HW_USB, KeystoreSource.SW_SEED); diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/IndexedTransactionForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/IndexedTransactionForm.java index 4dd71b81..db40c93a 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/IndexedTransactionForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/IndexedTransactionForm.java @@ -1,7 +1,7 @@ package com.sparrowwallet.sparrow.transaction; public abstract class IndexedTransactionForm extends TransactionForm { - private final int index; + private int index; public IndexedTransactionForm(TransactionData txdata, int index) { super(txdata); @@ -11,4 +11,8 @@ public abstract class IndexedTransactionForm extends TransactionForm { public int getIndex() { return index; } + + public void setIndex(int index) { + this.index = index; + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/InputController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/InputController.java index d6d30c35..b12a7dc8 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/InputController.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/InputController.java @@ -570,4 +570,11 @@ public class InputController extends TransactionFormController implements Initia updateScriptFields(event.getFinalTransaction().getInputs().get(inputForm.getIndex()), null); } } + + @Subscribe + public void psbtReordered(PSBTReorderedEvent event) { + if(event.getPsbt().equals(inputForm.getPsbt())) { + updateInputLegendFromWallet(inputForm.getTransactionInput(), null); + } + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java index c90ccb4f..602ae0ae 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputController.java @@ -9,6 +9,7 @@ import com.sparrowwallet.drongo.wallet.BlockTransaction; import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.control.*; +import com.sparrowwallet.sparrow.event.PSBTReorderedEvent; import com.sparrowwallet.sparrow.event.UnitFormatChangedEvent; import com.sparrowwallet.sparrow.event.BlockTransactionOutputsFetchedEvent; import com.sparrowwallet.sparrow.event.ViewTransactionEvent; @@ -178,4 +179,11 @@ public class OutputController extends TransactionFormController implements Initi public void unitFormatChanged(UnitFormatChangedEvent event) { value.refresh(event.getUnitFormat(), event.getBitcoinUnit()); } + + @Subscribe + public void psbtReordered(PSBTReorderedEvent event) { + if(event.getPsbt().equals(outputForm.getPsbt())) { + updateOutputLegendFromWallet(outputForm.getTransactionOutput(), null); + } + } } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java index 184d83c5..18974462 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/OutputForm.java @@ -9,26 +9,20 @@ import javafx.scene.Node; import java.io.IOException; public class OutputForm extends IndexedTransactionForm { - private final TransactionOutput transactionOutput; - private PSBTOutput psbtOutput; - public OutputForm(TransactionData txdata, PSBTOutput psbtOutput) { super(txdata, txdata.getPsbt().getPsbtOutputs().indexOf(psbtOutput)); - this.transactionOutput = txdata.getPsbt().getTransaction().getOutputs().get(txdata.getPsbt().getPsbtOutputs().indexOf(psbtOutput)); - this.psbtOutput = psbtOutput; } public OutputForm(TransactionData txdata, TransactionOutput transactionOutput) { super(txdata, txdata.getTransaction().getOutputs().indexOf(transactionOutput)); - this.transactionOutput = transactionOutput; } public TransactionOutput getTransactionOutput() { - return transactionOutput; - } + if(txdata.getTransaction() != null) { + return txdata.getTransaction().getOutputs().get(getIndex()); + } - public PSBTOutput getPsbtOutput() { - return psbtOutput; + return null; } public boolean isWalletConsolidation() { @@ -59,6 +53,6 @@ public class OutputForm extends IndexedTransactionForm { } public String toString() { - return "Output #" + transactionOutput.getIndex(); + return "Output #" + getIndex(); } } diff --git a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionController.java b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionController.java index 0ad686dd..9b71f396 100644 --- a/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionController.java +++ b/src/main/java/com/sparrowwallet/sparrow/transaction/TransactionController.java @@ -23,6 +23,7 @@ import javafx.scene.Parent; import javafx.scene.control.TreeCell; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; +import javafx.scene.input.*; import javafx.scene.layout.Pane; import org.controlsfx.control.MasterDetailPane; import org.slf4j.Logger; @@ -35,6 +36,7 @@ import java.util.stream.Collectors; public class TransactionController implements Initializable { private static final Logger log = LoggerFactory.getLogger(TransactionController.class); + private static final DataFormat JAVA_FORMAT = new DataFormat("application/x-java-serialized-object"); @FXML private Node tabContent; @@ -62,6 +64,9 @@ public class TransactionController implements Initializable { private boolean allInputsFetchedFromWallet; private boolean transactionsFetched; + private TreeItem draggedItem; + private TreeCell dropZone; + @Override public void initialize(URL location, ResourceBundle resources) { EventManager.get().register(this); @@ -174,6 +179,14 @@ public class TransactionController implements Initializable { setGraphic(TransactionDiagram.getPaymentGlyph()); } } + + setOnDragDetected(null); + setOnDragOver(null); + setOnDragDropped(null); + } else if(form.isEditable()) { + setOnDragDetected(event -> dragDetected(event, this)); + setOnDragOver(event -> dragOver(event, this)); + setOnDragDropped(event -> drop(event, this)); } } } @@ -463,6 +476,85 @@ public class TransactionController implements Initializable { this.initialIndex = initialIndex; } + private void dragDetected(MouseEvent event, TreeCell treeCell) { + draggedItem = treeCell.getTreeItem(); + + if(!draggedItem.getChildren().isEmpty()) { + return; + } + Dragboard db = treeCell.startDragAndDrop(TransferMode.MOVE); + + ClipboardContent content = new ClipboardContent(); + content.put(JAVA_FORMAT, draggedItem.getValue().toString()); + db.setContent(content); + db.setDragView(treeCell.snapshot(null, null)); + event.consume(); + } + + private void dragOver(DragEvent event, TreeCell treeCell) { + if(!event.getDragboard().hasContent(JAVA_FORMAT)) { + return; + } + TreeItem thisItem = treeCell.getTreeItem(); + + if(draggedItem == null || thisItem == null || thisItem == draggedItem) { + return; + } + if(draggedItem.getParent() == null || thisItem.getParent() == null) { + return; + } + if(draggedItem.getValue() instanceof InputForm && (thisItem.getValue() instanceof OutputForm || thisItem.getValue() instanceof OutputsForm)) { + return; + } + if(draggedItem.getValue() instanceof OutputForm && (thisItem.getValue() instanceof InputForm || thisItem.getValue() instanceof InputsForm)) { + return; + } + + event.acceptTransferModes(TransferMode.MOVE); + if(!Objects.equals(dropZone, treeCell)) { + this.dropZone = treeCell; + } + } + + private void drop(DragEvent event, TreeCell treeCell) { + Dragboard db = event.getDragboard(); + if(!db.hasContent(JAVA_FORMAT)) { + return; + } + + TreeItem thisItem = treeCell.getTreeItem(); + TreeItem droppedItemParent = draggedItem.getParent(); + + int fromIndex = droppedItemParent.getChildren().indexOf(draggedItem); + int toIndex = Objects.equals(droppedItemParent, thisItem) ? 0 : thisItem.getParent().getChildren().indexOf(thisItem); + droppedItemParent.getChildren().remove(draggedItem); + + if(Objects.equals(droppedItemParent, thisItem)) { + thisItem.getChildren().add(toIndex, draggedItem); + } else { + thisItem.getParent().getChildren().add(toIndex, draggedItem); + } + + PSBT psbt = getPSBT(); + if(draggedItem.getValue() instanceof InputForm) { + psbt.moveInput(fromIndex, toIndex); + } else if(draggedItem.getValue() instanceof OutputForm) { + psbt.moveOutput(fromIndex, toIndex); + } + + for(int i = 0; i < draggedItem.getParent().getChildren().size(); i++) { + if(draggedItem.getParent().getChildren().get(i).getValue() instanceof IndexedTransactionForm indexedTransactionForm) { + indexedTransactionForm.setIndex(i); + } + } + + txdata.setTransaction(psbt.getTransaction()); + txtree.getSelectionModel().select(draggedItem); + event.setDropCompleted(true); + + EventManager.get().post(new PSBTReorderedEvent(psbt)); + } + @Subscribe public void viewTransaction(ViewTransactionEvent event) { if(txdata.getTransaction().getTxId().equals(event.getTransaction().getTxId())) { @@ -594,4 +686,12 @@ public class TransactionController implements Initializable { fetchTransactions(); } } + + @Subscribe + public void psbtReordered(PSBTReorderedEvent event) { + if(event.getPsbt() == getPSBT()) { + txhex.setTransaction(getTransaction()); + highlightTxHex(); + } + } } \ No newline at end of file