Craig Raw
3 years ago
22 changed files with 410 additions and 35 deletions
@ -0,0 +1,218 @@ |
|||||
|
package com.sparrowwallet.sparrow.control; |
||||
|
|
||||
|
import com.sparrowwallet.drongo.KeyPurpose; |
||||
|
import com.sparrowwallet.drongo.wallet.Wallet; |
||||
|
import com.sparrowwallet.sparrow.AppServices; |
||||
|
import com.sparrowwallet.sparrow.wallet.*; |
||||
|
import javafx.beans.property.ReadOnlyObjectWrapper; |
||||
|
import javafx.collections.ListChangeListener; |
||||
|
import javafx.scene.control.*; |
||||
|
import javafx.scene.image.Image; |
||||
|
import javafx.scene.image.ImageView; |
||||
|
import javafx.scene.layout.VBox; |
||||
|
import org.controlsfx.control.textfield.TextFields; |
||||
|
import org.slf4j.Logger; |
||||
|
import org.slf4j.LoggerFactory; |
||||
|
import tornadofx.control.Field; |
||||
|
import tornadofx.control.Fieldset; |
||||
|
import tornadofx.control.Form; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
|
||||
|
public class SearchWalletDialog extends Dialog<Entry> { |
||||
|
private static final Logger log = LoggerFactory.getLogger(SearchWalletDialog.class); |
||||
|
|
||||
|
private final WalletForm walletForm; |
||||
|
private final TextField search; |
||||
|
private final CoinTreeTable results; |
||||
|
|
||||
|
public SearchWalletDialog(WalletForm walletForm) { |
||||
|
this.walletForm = walletForm; |
||||
|
|
||||
|
final DialogPane dialogPane = getDialogPane(); |
||||
|
dialogPane.getStylesheets().add(AppServices.class.getResource("general.css").toExternalForm()); |
||||
|
dialogPane.getStylesheets().add(AppServices.class.getResource("dialog.css").toExternalForm()); |
||||
|
dialogPane.getStylesheets().add(AppServices.class.getResource("wallet/wallet.css").toExternalForm()); |
||||
|
dialogPane.getStylesheets().add(AppServices.class.getResource("search.css").toExternalForm()); |
||||
|
AppServices.setStageIcon(dialogPane.getScene().getWindow()); |
||||
|
dialogPane.setHeaderText("Search Wallet"); |
||||
|
|
||||
|
Image image = new Image("image/sparrow-small.png", 50, 50, false, false); |
||||
|
if(!image.isError()) { |
||||
|
ImageView imageView = new ImageView(); |
||||
|
imageView.setSmooth(false); |
||||
|
imageView.setImage(image); |
||||
|
dialogPane.setGraphic(imageView); |
||||
|
} |
||||
|
|
||||
|
VBox vBox = new VBox(); |
||||
|
vBox.setSpacing(20); |
||||
|
|
||||
|
Form form = new Form(); |
||||
|
Fieldset fieldset = new Fieldset(); |
||||
|
fieldset.setText(""); |
||||
|
fieldset.setSpacing(10); |
||||
|
|
||||
|
Field searchField = new Field(); |
||||
|
searchField.setText("Search:"); |
||||
|
search = TextFields.createClearableTextField(); |
||||
|
search.setPromptText("Label, address, value or transaction ID"); |
||||
|
searchField.getInputs().add(search); |
||||
|
|
||||
|
fieldset.getChildren().addAll(searchField); |
||||
|
form.getChildren().add(fieldset); |
||||
|
|
||||
|
results = new CoinTreeTable(); |
||||
|
results.setShowRoot(false); |
||||
|
results.setPrefWidth(850); |
||||
|
results.setBitcoinUnit(walletForm.getWallet()); |
||||
|
results.setColumnResizePolicy(TreeTableView.CONSTRAINED_RESIZE_POLICY); |
||||
|
results.setPlaceholder(new Label("No results")); |
||||
|
|
||||
|
TreeTableColumn<Entry, String> typeColumn = new TreeTableColumn<>("Type"); |
||||
|
typeColumn.setCellValueFactory((TreeTableColumn.CellDataFeatures<Entry, String> param) -> { |
||||
|
return new ReadOnlyObjectWrapper<>(param.getValue().getValue().getEntryType()); |
||||
|
}); |
||||
|
results.getColumns().add(typeColumn); |
||||
|
|
||||
|
TreeTableColumn<Entry, Entry> entryCol = new TreeTableColumn<>("Date / Address / Output"); |
||||
|
entryCol.setCellValueFactory((TreeTableColumn.CellDataFeatures<Entry, Entry> param) -> { |
||||
|
return new ReadOnlyObjectWrapper<>(param.getValue().getValue()); |
||||
|
}); |
||||
|
entryCol.setCellFactory(p -> new SearchEntryCell()); |
||||
|
String address = walletForm.getNodeEntry(KeyPurpose.RECEIVE).getAddress().toString(); |
||||
|
if(address != null) { |
||||
|
entryCol.setMinWidth(TextUtils.computeTextWidth(AppServices.getMonospaceFont(), address, 0.0)); |
||||
|
} |
||||
|
results.getColumns().add(entryCol); |
||||
|
|
||||
|
TreeTableColumn<Entry, String> labelCol = new TreeTableColumn<>("Label"); |
||||
|
labelCol.setCellValueFactory((TreeTableColumn.CellDataFeatures<Entry, String> param) -> { |
||||
|
return param.getValue().getValue().labelProperty(); |
||||
|
}); |
||||
|
labelCol.setCellFactory(p -> new SearchLabelCell()); |
||||
|
results.getColumns().add(labelCol); |
||||
|
|
||||
|
TreeTableColumn<Entry, Number> amountCol = new TreeTableColumn<>("Value"); |
||||
|
amountCol.setCellValueFactory((TreeTableColumn.CellDataFeatures<Entry, Number> param) -> { |
||||
|
return new ReadOnlyObjectWrapper<>(param.getValue().getValue().getValue()); |
||||
|
}); |
||||
|
amountCol.setCellFactory(p -> new CoinCell()); |
||||
|
results.getColumns().add(amountCol); |
||||
|
|
||||
|
vBox.getChildren().addAll(form, results); |
||||
|
dialogPane.setContent(vBox); |
||||
|
|
||||
|
ButtonType showButtonType = new javafx.scene.control.ButtonType("Show", ButtonBar.ButtonData.APPLY); |
||||
|
ButtonType cancelButtonType = new javafx.scene.control.ButtonType("Cancel", ButtonBar.ButtonData.CANCEL_CLOSE); |
||||
|
|
||||
|
dialogPane.getButtonTypes().addAll(cancelButtonType, showButtonType); |
||||
|
|
||||
|
Button showButton = (Button) dialogPane.lookupButton(showButtonType); |
||||
|
showButton.setDefaultButton(true); |
||||
|
showButton.setDisable(true); |
||||
|
|
||||
|
setResultConverter(buttonType -> buttonType == showButtonType ? results.getSelectionModel().getSelectedItem().getValue() : null); |
||||
|
|
||||
|
results.getSelectionModel().getSelectedIndices().addListener((ListChangeListener<Integer>) c -> { |
||||
|
showButton.setDisable(results.getSelectionModel().getSelectedCells().isEmpty()); |
||||
|
}); |
||||
|
|
||||
|
search.textProperty().addListener((observable, oldValue, newValue) -> { |
||||
|
searchWallet(newValue.toLowerCase()); |
||||
|
}); |
||||
|
|
||||
|
setResizable(true); |
||||
|
} |
||||
|
|
||||
|
private void searchWallet(String searchText) { |
||||
|
List<Entry> matchingEntries = new ArrayList<>(); |
||||
|
|
||||
|
if(!searchText.isEmpty()) { |
||||
|
Long searchValue = null; |
||||
|
|
||||
|
try { |
||||
|
searchValue = Math.abs(Long.parseLong(searchText)); |
||||
|
} catch(NumberFormatException e) { |
||||
|
//ignore
|
||||
|
} |
||||
|
|
||||
|
WalletTransactionsEntry walletTransactionsEntry = walletForm.getWalletTransactionsEntry(); |
||||
|
for(Entry entry : walletTransactionsEntry.getChildren()) { |
||||
|
if(entry instanceof TransactionEntry transactionEntry) { |
||||
|
if(transactionEntry.getBlockTransaction().getHash().toString().equals(searchText) || |
||||
|
(transactionEntry.getLabel() != null && transactionEntry.getLabel().toLowerCase().contains(searchText)) || |
||||
|
(transactionEntry.getValue() != null && searchValue != null && Math.abs(transactionEntry.getValue()) == searchValue)) { |
||||
|
matchingEntries.add(entry); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
for(KeyPurpose keyPurpose : KeyPurpose.DEFAULT_PURPOSES) { |
||||
|
NodeEntry purposeEntry = walletForm.getNodeEntry(keyPurpose); |
||||
|
for(Entry entry : purposeEntry.getChildren()) { |
||||
|
if(entry instanceof NodeEntry nodeEntry) { |
||||
|
if(nodeEntry.getAddress().toString().contains(searchText) || |
||||
|
(nodeEntry.getLabel() != null && nodeEntry.getLabel().toLowerCase().contains(searchText)) || |
||||
|
(nodeEntry.getValue() != null && searchValue != null && Math.abs(nodeEntry.getValue()) == searchValue)) { |
||||
|
matchingEntries.add(entry); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
WalletUtxosEntry walletUtxosEntry = walletForm.getWalletUtxosEntry(); |
||||
|
for(Entry entry : walletUtxosEntry.getChildren()) { |
||||
|
if(entry instanceof HashIndexEntry hashIndexEntry) { |
||||
|
if(hashIndexEntry.getBlockTransaction().getHash().toString().equals(searchText) || |
||||
|
(hashIndexEntry.getLabel() != null && hashIndexEntry.getLabel().toLowerCase().contains(searchText)) || |
||||
|
(hashIndexEntry.getValue() != null && searchValue != null && Math.abs(hashIndexEntry.getValue()) == searchValue)) { |
||||
|
matchingEntries.add(entry); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
SearchWalletEntry rootEntry = new SearchWalletEntry(walletForm.getWallet(), matchingEntries); |
||||
|
RecursiveTreeItem<Entry> rootItem = new RecursiveTreeItem<>(rootEntry, Entry::getChildren); |
||||
|
results.setRoot(rootItem); |
||||
|
} |
||||
|
|
||||
|
private static class SearchWalletEntry extends Entry { |
||||
|
public SearchWalletEntry(Wallet wallet, List<Entry> entries) { |
||||
|
super(wallet, wallet.getName(), entries); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Long getValue() { |
||||
|
return 0L; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getEntryType() { |
||||
|
return "Search Wallet Results"; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Function getWalletFunction() { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static class SearchEntryCell extends EntryCell { |
||||
|
@Override |
||||
|
protected void updateItem(Entry entry, boolean empty) { |
||||
|
super.updateItem(entry, empty); |
||||
|
setContextMenu(null); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static class SearchLabelCell extends LabelCell { |
||||
|
@Override |
||||
|
public void updateItem(String label, boolean empty) { |
||||
|
super.updateItem(label, empty); |
||||
|
setContextMenu(null); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
package com.sparrowwallet.sparrow.event; |
||||
|
|
||||
|
import com.sparrowwallet.drongo.wallet.Wallet; |
||||
|
import com.sparrowwallet.sparrow.wallet.Function; |
||||
|
|
||||
|
public class FunctionActionEvent { |
||||
|
private final Function function; |
||||
|
private final Wallet wallet; |
||||
|
|
||||
|
public FunctionActionEvent(Function function, Wallet wallet) { |
||||
|
this.function = function; |
||||
|
this.wallet = wallet; |
||||
|
} |
||||
|
|
||||
|
public Function getFunction() { |
||||
|
return function; |
||||
|
} |
||||
|
|
||||
|
public Wallet getWallet() { |
||||
|
return wallet; |
||||
|
} |
||||
|
|
||||
|
public boolean selectFunction() { |
||||
|
return true; |
||||
|
} |
||||
|
} |
@ -1,20 +1,15 @@ |
|||||
package com.sparrowwallet.sparrow.event; |
package com.sparrowwallet.sparrow.event; |
||||
|
|
||||
import com.sparrowwallet.drongo.wallet.Wallet; |
import com.sparrowwallet.drongo.wallet.Wallet; |
||||
|
import com.sparrowwallet.sparrow.wallet.Function; |
||||
import com.sparrowwallet.sparrow.wallet.NodeEntry; |
import com.sparrowwallet.sparrow.wallet.NodeEntry; |
||||
|
|
||||
public class ReceiveActionEvent { |
public class ReceiveActionEvent extends FunctionActionEvent { |
||||
private final Wallet wallet; |
|
||||
|
|
||||
public ReceiveActionEvent(NodeEntry receiveEntry) { |
public ReceiveActionEvent(NodeEntry receiveEntry) { |
||||
this.wallet = receiveEntry.getWallet(); |
super(Function.RECEIVE, receiveEntry.getWallet()); |
||||
} |
} |
||||
|
|
||||
public ReceiveActionEvent(Wallet wallet) { |
public ReceiveActionEvent(Wallet wallet) { |
||||
this.wallet = wallet; |
super(Function.RECEIVE, wallet); |
||||
} |
|
||||
|
|
||||
public Wallet getWallet() { |
|
||||
return wallet; |
|
||||
} |
} |
||||
} |
} |
||||
|
@ -0,0 +1,20 @@ |
|||||
|
package com.sparrowwallet.sparrow.event; |
||||
|
|
||||
|
import com.sparrowwallet.drongo.wallet.Wallet; |
||||
|
import com.sparrowwallet.sparrow.wallet.Entry; |
||||
|
|
||||
|
public class SelectEntryEvent { |
||||
|
private final Entry entry; |
||||
|
|
||||
|
public SelectEntryEvent(Entry entry) { |
||||
|
this.entry = entry; |
||||
|
} |
||||
|
|
||||
|
public Entry getEntry() { |
||||
|
return entry; |
||||
|
} |
||||
|
|
||||
|
public Wallet getWallet() { |
||||
|
return entry.getWallet(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,3 @@ |
|||||
|
.cell-actions { |
||||
|
visibility: hidden; |
||||
|
} |
Loading…
Reference in new issue